This post features a really simple skia graphics but details instructions for installing react native skia on the expo go and expo web. Guide builds on top of the expo router tabs demo app and saves you time and frustration.

Intros, what’s what?

Skia is a powerful open source graphics engine and library. It’s widely used in systems like Google Chrome, Android, iOS and many others.

Expo is a platform for building native apps for Android, iOS and the web with javascript/typescript and React Native. When devs go with expo instead of react native, they usually choose managed workflow. Managed workflow means most or all of the native code is hidden from you, you don’t need to worry about that, just type your React code and you’re done.

Expo Go is a mobile client app which lets you open and test projects while developing them. When you make changes to your code, those changes are immediately visible in the Expo Go app on your mobile device without needing a full reload of the app, preserving the current state. I believe other word for this feature is hot reload.

Expo Router is a file-based router for React Native and web applications. It allows you to manage navigation between screens in your app. Architecture of the routes and layout is very similar, if not the same as next.js app router.


Configured Android SDK on your machine. Take a look at windows or linux instructions if you need step by step guide (also recorded on Youtube).

Initialize the project

You can skip this step if you already have setup an expo project. I’m using the expo tabs template which includes a basic tab navigator setup, providing a structured layout and navigation system right from the start.

npx create-expo-app@latest --template tabs@50

Install Skia

When installing dependencies on expo, usually you would install with npx expo install instead of npm or yarn. Reason for this approach is compatibility and automatic configuration. When issuing expo install, expo will pull the version which is known to work correctly with the version of sdk you are using. Also, expo will sometimes modify the native code or configuration files accordingly.

In an ideal world, right?

From my experience, this is not always the case. In the case of skia as authors mention:

Metro and expo-router support is available from v0.1.240 and onwards. If you are using v0.1.221 (recommended version for Expo SDK 50), you can use this patch (using patch-package.

Version of sdk 50 will install 0.1.221 which does not work without an additional patch.

My solution is to use a newer version and ignore expo-doctor warnings.

npm i --save @shopify/[email protected]
npx expo install react-native-reanimated

Skia depends on react-native-reanimated, which is not installed without explicitly declaring it in package.json.

Configure Skia

React native skia does not work without canvaskit.wasm, which needs to be stored somewhere in the expo app. Since I’m using the expo router setup, static files can be stored in public directory.

Adding `”postinstall”: “npx setup-skia-web public”` makes the wasm file accessible.

There’s one fun error that occurs after the setup. I’ve lost an hour trying to debug and polyfill:

The package at “node_modules\canvaskit-wasm\bin\full\canvaskit.js” attempted to import the Node standard library module “fs”.
It failed because the native React runtime does not include the Node standard library.

Self explanatory message, react native needs a workaround for standard node library like fs and path.

React Native does not include Node.js’s standard library. Usual solution would be to polyfill the standard library. But the real culprit here is the canvaskit-wasm module setup and not the lack of the standard Node library. Thanks to kimchouard, there’s a workaround for this issue (just copying the piece here to see what’s going on):

 * Original code by
const fs = require('fs');
const path = require('path');

const packageJsonPath = path.join(__dirname, 'node_modules', 'canvaskit-wasm', 'package.json');
const packageJson = require(packageJsonPath);

packageJson.browser = {
    fs: false,
    path: false,
    os: false,

fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));

canvaskit-wasm has no place using fs in native and web since we point to wasm file and also run npx setup-skia-web public as part of postinstall.

Create or modify metro.config.js and add the following:

const { getDefaultConfig } = require('expo/metro-config');

const config = getDefaultConfig(__dirname, {
    isCSSEnabled: true,

config.transformer.getTransformOptions = async () => ({
    transform: {
        experimentalImportSupport: false,
        inlineRequires: true,

module.exports = config;

This code customizes the Metro bundler configuration by adding support for WebAssembly (.wasm) files as assets. It also improves performance by inlining require() calls and disables experimental import features. I don’t know why it doesn’t work without custom transform rules.

Making it work for the Web

This is where LoadSkiaWeb comes handy. I’d recommend adding this code in the root _layout if you need it accessible in your entire app.

useEffect(() => {
    if (Platform.OS === 'web') {
      LoadSkiaWeb({ locateFile: () => '/canvaskit.wasm' })
        .then(() => {
        .catch((err) => console.error(err));
    } else {
  }, []);


Sometimes expo acts fishy for no apparent reason (like the `Cannot read properties of undefined (reading ‘Matrix’)`). Then the only solution is to clear the bundler cache and reinstall/rebuild the app. Anyway, all source code to install and configure react native skia on web and on native is available at my github repo. Also I recorded a YT tutorial to make it easier to follow the setup.

By the way, curious about how I know so much? I read a lot! Get yourself a Kindle and enjoy the reading experience of a real book. I still use my Paperwhite 4 – battery lasts forever, it’s very lightweight and has an adjustable reading light.

Alternatively, subscribe to O’Reilly books and get access to a large database of reading and video material. Unfortunately O’Reilly isn’t suited for Kindle, but they offer a handy app you can download and read from your phone. Anyway, I use both my Kindle and O’Reilly. I read many books at once, and never read from beginning to end, but that’s my defect 😀

Categories: Programming

Notify of
Inline Feedbacks
View all comments