Deploying a WASM-Powered React App on Vercel

A step-by-step guide to getting your WASM-react app out into the world! Link to heading

1.png

Over the past few weeks, I have been building a React app supercharged by functions written in Rust. Now, first things first, a little background…

What Is Web Assembly? Link to heading

According to webassembly.org, it is defined as the following

“WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.”

In short, you can compile your high-performance code in statically compiled languages like C++, Rust, or Go into a web assembly format. It is highly portable and is expected to execute at native speeds.

Needless to say, I was very excited about this relatively new technology. Therefore, I created a React app that uses web assembly code as a “pseudo-backend.” Although, in my case, one might say it is an overkill, it is a great learning opportunity nevertheless.

Now let’s get started with the guide.

Directory Structure Link to heading

’’' src/ └── components ├── ComposePrompt ├── Controls ├── Edge ├── Node └── styles wasm-parser/ └── src/ ’''

As you might expect, we have two different subpackages within the project:

The package created by the create-react-app. It is managed by npm and there will be an associated package.json. The library was created and managed by cargo — wasm-parser. In this directory, the Rust code that needs to be compiled into web assembly can be specified.

Calling WASM Functions on React Link to heading

Various detailed articles explain how you can call web assembly compiled functions from javascript and vice-versa. In any case, I will summarise the broad details.

Cargo library — wasm-parser Link to heading

#[wasm_bindgen]
pub fn generate_configuration(node_data: String, edge_data: String) -> String {
    let nodes: Vec<Node> = parse_nodes(&node_data);
    let edges: Vec<Edge> = parse_edges(&edge_data);
    let docker_compose_obj = generate_dockercompose_yml(nodes, edges);
    let output_string = serde_yaml::to_string(&docker_compose_obj).unwrap();
    return output_string;
}

The function generate_configuration is called from the React client. The macro used before the function — wasm_bindgen — enables high-level interaction between JS- and WASM-compiled code.

Compiling to WASM Link to heading

The Rust library can be compiled into WASM using a tool called wasm-pack. The build command can be directly added to the npm package.json file for convenience.

"scripts": {
    ...    
    "build:wasm": "cd wasm-parser && ~/.cargo/bin/wasm-pack build --target web --out-dir ./wasm-build"
}

Furthermore, the compiled package needs to be specified as a dependency of the node package.

"dependencies": {
    ...
    "wasm-parser": "file:./wasm-parser/wasm-build"
}

React client Link to heading

On the client side, the WASM-compiled function can be easily called by importing it from the dependency that was added to the package.json file earlier.


// WASM modules
import init, { generate_configuration } from "wasm-parser";
// Calling the function 
init().then(() => {
    const rusty_str = generate_configuration(stringifiedNodes, stringifiedEdges);
});

Deploying to Vercel Link to heading

Now for the fun part of deploying your app for everyone to enjoy it. For this example, I have used Vercel as the platform to set up a continuous deployment pipeline and subsequently deploy a production build of my app. To avoid vendor lock-in, the same procedure can be followed to deploy it on other platforms.

Create a Vercel account Link to heading

The first step is to create an account on Vercel and link it to GitHub. This ensures that the latest changes are reflected in the deployed version. Note: This is not necessarily the best option in production, but it suffices to do so for a hobby project. Add your GitHub project from the generated list of repositories on Vercel. Select the template based on the framework that you based your front-end client on. In my case, I used the create-react-app template.

Modify the build step Link to heading

We need to override the default build step to build and link the WASM-compiled code to your React client. To facilitate and standardise the build step, I created a custom bash script that can be run to complete the production build. I will explain each step below:

Install Rust Link to heading

Vercel uses Amazon Linux 2 as a base image for its environments. Rustup must be installed in the environment before we can compile to WASM.

echo "Installing Rustup..."
# Install Rustup (compiler)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Adding binaries to path
source "$HOME/.cargo/env"

Install wasm-pack Link to heading

echo "Installing wasm-pack..."
# Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y

Build the WASM package Link to heading

echo "Building wasm-parser..."
# Build wasm-parser 
npm run build:wasm

Complete the production build Link to heading

echo "Build static frontend client..."
# Build static html for the react client
npm run build

Voilà! You are Done. Link to heading

If the build logs are clean and you don’t have any errors, your WASM-powered React app should now be deployed!

Want to Connect? Link to heading

Thank you for reading my article. You can also find me on LinkedIn and my work on GitHub.