React + Rust + Wasm: Introduction

Neo Quest | Nikhil Gupta / October 21, 2022
3 min read
Summary
In this article, we will create a demo React Application that uses a Rust WASM library.
Basic React App Setup
Use create-react-app with Typescript support like so:
npx create-react-app rust-wasm-demo --template typescript
To run it, simply do the following:
cd rust-wasm-demo
npm start
Rust WASM Setup
First, install the standard Rust toolchain using the instructions here.
Next, install wasm-pack
Finally, create a Rust library like so:
cargo new rust-wasm-lib --lib
Rust Sample Code
Now, let's create a basic function in Rust that will be called by our React app.
// lib.rs
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[test]
fn add_test() {
assert_eq!(1 + 1, add(1, 1));
}
The second function is a unit test in Rust. You can run it natively like so:
cd rust-wasm-lib
cargo test
Next, we will create a binding that specifies how a function can be invoked from JS and its return semantics.
Export Rust Function to WASM
We will use wasm-bindgen` to export our function. To do so, let's add it to our Cargo dependencies:
cargo add wasm-bindgen
Then, let's export the function like so:
// lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[test]
fn add_test() {
assert_eq!(1 + 1, add(1, 1));
}
Build WASM
First, let's change the crate-type to cdylib so that it could be compiled to WASM:
# Cargo.toml
[package]
name = "rust-wasm-lib"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.83"
Now, we will use wasm-pack installed above to build our WASM library.
wasm-pack build --target web
This will create a pkg
directory with WASM and JS files.
Install WASM
Now, we can go back to the main folder and install our built WASM library:
npm i ./rust-wasm-lib/pkg
Call WASM from the demo app
Finally, let's call the exported add
function from our App.ts
file like so:
// App.ts
import React, { useEffect, useState } from 'react';
import logo from './logo.svg';
import init, { add } from "rust-wasm-lib";
import './App.css';
function App() {
const [result, setResult] = useState(0);
useEffect(() => {
init().then(() => setResult(add(1, 2)));
}, [setResult])
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<p>1 + 2 = {result}</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
You should see 1 + 2 = 3
on the screen. :)