DEV Community

Ana Bujan
Ana Bujan

Posted on • Originally published at amarjanica.com on

How to Install React Native Skia with Expo for Web and Native

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.

Requirements

Configured Android SDK on your machine. Take a look at windowsor linuxinstructions 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
Enter fullscreen mode Exit fullscreen mode

Install Skia

When installing dependencies on expo, usually you would install with npx expo install instead of npmor 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.

https://shopify.github.io/react-native-skia/docs/getting-started/web/#expo

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/react-native-skia@0.1.240
npx expo install react-native-reanimated
Enter fullscreen mode Exit fullscreen mode

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.

https://docs.expo.dev/workflow/using-libraries/#using-third-party-libraries

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 https://github.com/kimchouard/rn-skia-metro-web-example/blob/main/path-fs-canvaskit-postinstall.js
 */
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));
Enter fullscreen mode Exit fullscreen mode

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.resolver.assetExts.push('wasm');
config.transformer.getTransformOptions = async () => ({
    transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
    },
});

module.exports = config;
Enter fullscreen mode Exit fullscreen mode

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.

_layout.ts
useEffect(() => {
    if (Platform.OS === 'web') {
      LoadSkiaWeb({ locateFile: () => '/canvaskit.wasm' })
        .then(() => {
          setLoaded(true);
        })
        .catch((err) => console.error(err));
    } else {
      setLoaded(true);
    }
  }, []);
Enter fullscreen mode Exit fullscreen mode

Conclusion

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.

Top comments (0)