How React Native Skia Grew From Alpha to Cross-Platform Renderer

It has been one year since Shopify released the first alpha of React Native Skia. In the twelve months since, the team shipped 90 releases and moved from an initial integration that merely fit React Native and Skia together to a purpose-built declarative architecture. Here is a look at the technical journey, the bottlenecks that shaped the API, and the roadmap for the library heading into 2023.

One Renderer for Every Platform

At the core of React Native Skia is a custom React renderer that expresses Skia drawings declaratively. Because the renderer has no dependency on the DOM or any native API, the same code runs on iOS, Android, the Web, and Node.js. Adding new platforms is straightforward wherever React runs and a Skia host API is available.

A gif showing a breath animation on each different platform: iOS, Android, and web
The React renderer runs on iOS, Android and Web.
Because the renderer is not coupled with DOM or Native APIs, we can use it for testing on Node.js.

On native platforms, the host API is exposed to JavaScript through the JavaScript Interface (JSI). On the Web, Skia comes via CanvasKit, a WebAssembly build of the engine. The team deliberately made the host API fully compatible with CanvasKit, whose concise design is based on the Flutter drawing API. That compatibility produced an immediate payoff: the original project announcement's graphic motions were written in React Native Skia itself, rendered through Remotion on the Web.

Reaching a wider audience meant making the library work with Expo. React Native Skia now runs out of the box with Expo dev clients, is included in the Expo Go client, and ships with full React Native Web support.

A gif showing an Expo screen for universal-skia-demo code on the left hand side and the corresponding code executing on the right
Thanks to Expo, all you need to get started with React Native Skia is a Web browser

GPU integration differs by platform: Metal on iOS, OpenGL on Android and the Web. The declarative API closely tracks Skia's imperative API with some sensible additions. A paint object handles cascading effects like opacity, and React Native developers will find familiar utilities: the standard React Native transform syntax can be used instead of matrices, and image resizing follows the same conventions.

The Move to UI Thread Rendering

Early demos were compelling, but the first alpha exposed two significant bottlenecks. First, the JavaScript thread was involved in collecting Skia drawing commands. Screen transitions with multiple Skia canvases locked the JS thread for each one, causing noticeable delays on low-end devices. Second, JSI allocations became a problem at scale: a drawing with thousands of components meant thousands of object allocations and invocations, and the overhead of JSI calls accumulated into serious slowdowns.

The fix came in the form of a new API called Skia DOM. Despite its generic name, it is the foundation of the library's architecture. Skia DOM expresses any Skia drawing declaratively and is both platform- and framework-agnostic. It is implemented in C++ on iOS and Android and in JavaScript for React Native Web, but depends on nothing from React itself.

Skia DOM maintains a source of truth for the drawing state. When something changes, only the necessary parts are recomputed — nothing more. That granular invalidation is what unlocks the performance levels the team was after.

The Skia DOM API allows to execute Skia drawings outside the JavaScript thread.
The Skia DOM API allows to execute Skia drawings outside the JavaScript thread.

The rendering pipeline now looks like this:

  1. The React reconciler builds the SkiaDOM, a declarative representation of a Skia drawing, via JSI.
  2. SkiaDOM has no dependencies on the JavaScript thread, so it can always draw on the UI thread with a fast time-to-first-frame.
  3. Each value is computed only once. Updates can arrive from the JS or animation thread, and each update recomputes just the affected parts of the drawing.
  4. The full Skia API remains available through a thin JSI layer, useful for building objects like paths or shaders.

The project drew inspiration from existing Skia ecosystem tools like CanvasKit, and Skia DOM now serves as a declarative drawing model that other projects could reuse beyond React.

Extensibility and Quality Assurance

To keep React Native Skia a healthy open-source project, the team built extension points and invested heavily in testing. Library authors can hook into the C++ API to add their own Skia modules. One community project uses that extensibility to integrate Skottie, a Lottie player implemented in Skia, without adding it to the core bundle. That keeps the core library small while allowing developers to opt into extra modules.

A gif of 5 different coloured square lego blocks sliding around each other
Skottie is an implementation of Lottie in Skia

The most ambitious use case so far has come from the Margelo agency and its work on React Native Vision Camera. That project applies Skia image filters and effects to camera frames in real time, processing frames on the UI thread via JavaScript plugins.

A gif showing how the Skia shader applies it’s image filters to a smartphone camera.
A Skia shader is applied on every camera frame

Testing spans TypeScript and C++ with static analysis for both languages, plus an end-to-end suite that draws each example on iOS, Android, and Web and verifies the output is correct and identical across platforms. The suite can also check Skia code that produces a non-drawing result, such as a path object. Comparing results across platforms has surfaced insights about Skia itself — for example, how font rendering varies slightly by platform. The test environment was built incrementally, starting with Node tests and layering in iOS and Android support.

A gif showing a terminal window running tests. On the right hand side of the image is a phone showing the tests running.
Tests are run on iOS, Android, and Web and images are checked for correctness

Documentation is built with Docusaurus and leverages Shiki Twoslash to run TypeScript compiler checks on all code examples. Every snippet is statically verified to compile against the current version, and the rendered examples are included in the test suite.

A screenshot of the indices page on React Native Skia Docs
Thanks to Docusaurus, documentation examples are also checked for correctness.

What Is Next for 2023

With UI thread rendering now solid, animations are the next focus. Worklets from Reanimated have been prototyped for Skia animations, and the team is pursuing smooth animation that does not depend on the availability of the JavaScript thread. A deeper UI-thread-level integration between Skia and Reanimated is also on the table; the two libraries benefit from close coordination.

Beyond animations, the paragraph module will enable advanced text layouts. That unlocks new use cases such as complex text animations not currently possible in React Native, plus rich text layouts that sit alongside existing Skia drawings.

A gif showing code on the left hand side and the result on the left which is a paragraph automatically adjusting upon resize
Sneak peek at the Skia paragraph module in React Native

The project discussion board is open for feedback on where Skia could take your React Native app in the coming year.