WASM in Create-React-App 4 in 5mn (without ejecting)

nicolasrannou

Nicolas Rannou

Posted on December 22, 2020

WASM in Create-React-App 4 in 5mn (without ejecting)

First word

In this post, we will write some rust code, compile it to WASM and run it from a CRA4 application without ejecting.

Rust to WASM FTW

Why Rust? It's cool. There is a lot of hype around it so I decided to give it a go. The rust book and rust by examples were pretty useful to get an understanding of the basics.

Once I got my head around the basic concepts, time to look into the conversion to WASM. There is a lot of nice documentation out there (from the Rust official documentation of course ;)).

The recommendation is to use wasm-pack. That allows you to build and ship an importable WASM module in less than 2mn.

Create the project

$> wasm-pack new my-wasm-project
$> cd my-wasm-project
Enter fullscreen mode Exit fullscreen mode

Package and public (details)

$> wasm-pack build --scope nicolasrannou
$> cd pkg
/*make sure the package.json includes the right files. At the time I write this post some .js and .wasm files were missing.*/
$> npm publish --access=public
Enter fullscreen mode Exit fullscreen mode

CRACO

To load the exported module I chose CRACO because it appears Create-react-app-rewired is not really maintained anymore and doesn't work well with CRA4.

Adding dependencies in your CRA4 project

$> cd my-cra4-project
$> yarn add @nicolasrannou/my-wasm-project
$> yarn add @craco/craco
$> yarn add wasm-loader
Enter fullscreen mode Exit fullscreen mode

Do not forget to update package.json by replacing react-scripts by craco.

Setup the config file

$> cat craco.config.js

const { addBeforeLoader, loaderByName } = require('@craco/craco');

module.exports = {
  webpack: {
    configure: (webpackConfig) => {
      const wasmExtensionRegExp = /\.wasm$/;
      webpackConfig.resolve.extensions.push('.wasm');

      webpackConfig.module.rules.forEach((rule) => {
        (rule.oneOf || []).forEach((oneOf) => {
          if (oneOf.loader && oneOf.loader.indexOf('file-loader') >= 0) {
            oneOf.exclude.push(wasmExtensionRegExp);
          }
        });
      });

      const wasmLoader = {
        test: /\.wasm$/,
        exclude: /node_modules/,
        loaders: ['wasm-loader'],
      };

      addBeforeLoader(webpackConfig, loaderByName('file-loader'), wasmLoader);

      return webpackConfig;
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Import the WASM code

$> cat App.tsx
...
  useEffect(async () => {
    const promise = await import("@nicolasrannou/my-wasm-project");

    promise.greet();
  }, []);
...
Enter fullscreen mode Exit fullscreen mode

šŸ‘‹ Until next time!

šŸ’– šŸ’Ŗ šŸ™… šŸš©
nicolasrannou
Nicolas Rannou

Posted on December 22, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related