- Published on
How to create a React app with Rust and WebAssembly
Table of Contents
- Summary
- What is WebAssembly?
- Create a React App with create-react-app
- Create Rust library for Wasm
- Create Rust library with cargo.
- Implement a Rust function that you want to call from JavaScript.
- Wrap the function with wasm-bindgen to export it as Wasm.
- Build as Wasm library with wasm-pack
- Call the Wasm function from the React app.
- Advanced topics
- Conclusion
Summary
In this article, I'll introduce followings through creating simple demo application.
- How to create a React app quickly with create-react-app.
- How to create a Wasm library with Rust.
- How to combile a React app with Wasm library.
You can see all code in tkat0/react-wasm-tutorial.
What is WebAssembly?
WebAssembly is a new type of code that can be run in modern web browsers — it is a low-level assembly-like language with a compact binary format that runs with near-native performance and provides languages such as C/C++, C# and Rust with a compilation target so that they can run on the web. It is also designed to run alongside JavaScript, allowing both to work together.
Create a React App with create-react-app
You can create a React app with create-react-app with one command.
$ npx create-react-app react-wasm-tutorial --template typescript
...
Success! Created react-wasm-tutorial at /Users/tkat0/GitHub/react-wasm-tutorial
Then you can launch the app.
$ cd react-wasm-tutorial
$ npm start
...
Starting the development server...
Compiled successfully!
You can now view react-wasm-tutorial in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.0.153:3000

import and call
import React, { useEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
  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>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;
Today, we'll add a Wasm library to this react app. I recommend using this for creating a demo like this.
Create Rust library for Wasm
To add Wasm to the React app, you need to follow these steps.
- Create Rust library with cargo.
- Implement a Rust function that you want to call from JavaScript.
- Wrap the function with wasm-bindgento export it as Wasm.
- Build as Wasm library with wasm-pack.
- Call the Wasm function from the React app.
Create Rust library with cargo.
Create Rust library with cargo.
$ cargo new wasm-lib --lib
     Created library `wasm-lib` package
Implement a Rust function that you want to call from JavaScript.
Simply, we'll implement add function and call it from JavaScript.
// lib.rs
fn add(a: i32, b: i32) -> i32 {
    a + b
}
#[test]
fn add_test() {
    assert_eq!(1 + 1, add(1, 1));
}
You can run an unittest like this. In this time, the function is built as native environment not Wasm.
$ cd wasm-lib
$ cargo test
   Compiling wasm-lib v0.1.0 (/Users/tkat0/GitHub/react-wasm-tutorial/wasm-lib)
...
running 1 test
test add_test ... ok
Wrap the function with wasm-bindgen to export it as Wasm.
wasm-bindgen is Rust library that facilitate high-level interactions between Wasm and JavaScript. For example, you can call Rust(Wasm) from JavaScript, and vice versa.
To add wasm-bindgen dependency, you need to add it to Cargo.toml.
[package]
name = "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.78"
Then, Let's wrap the function with wasm-bindgen. Notice that only public function can be exported.
(updated Jan 2, 2022: thank you @tardisgallifrey)
+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 as Wasm library with wasm-pack
By using wasm-bindgen, you can build Rust as Wasm. However, To load and run Wasm from JavaScript, You need some JavaScript boilerplate codes (like WebAssembly.instantiate).
To do that, you can use wasm-pack!
At first, you need to install it.
$ cd ..
$ cargo install wasm-pack
$ wasm-pack --version
wasm-pack 0.10.2
Then, let's add npm script to call it by npm run build:wasm.
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
+    "build:wasm": "cd wasm-lib && wasm-pack build --target web --out-dir pkg",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
Finally, you can build Rust as Wasm and generate some boilerplate codes.
$ npm run build:wasm
[INFO]: 🎯  Checking for the Wasm target...
[INFO]: 🌀  Compiling to Wasm...
...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: ✨   Done in 1.58s
[INFO]: 📦   Your wasm pkg is ready to publish at /Users/tkat0/GitHub/react-wasm-tutorial/wasm-lib/pkg.
Let's check the output directory generated by wasm-pack.
You noticed package.json file was generated.
$ tree wasm-lib/pkg
├── package.json
├── wasm_lib.d.ts
├── wasm_lib.js
├── wasm_lib_bg.wasm
└── wasm_lib_bg.wasm.d.ts
So you can install the Wasm library to other project easily. Let's install it to the React app.
$ npm install ./wasm-lib/pkg
The library is added like this. Ofcourse, you can publish the library in npm.
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.1",
    "@testing-library/react": "^12.1.2",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.4.0",
    "@types/node": "^16.11.19",
    "@types/react": "^17.0.38",
    "@types/react-dom": "^17.0.11",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "5.0.0",
    "typescript": "^4.5.4",
+    "wasm-lib": "file:wasm-lib/pkg",
    "web-vitals": "^2.1.2"
  },
Call the Wasm function from the React app.
You can call the Wasm library like this.
import and call
import React, { useEffect, useState } from 'react';
+import init, { add } from "wasm-lib";
import logo from './logo.svg';
import './App.css';
function App() {
+  const [ans, setAns] = useState(0);
+  useEffect(() => {
+    init().then(() => {
+      setAns(add(1, 1));
+    })
+  }, [])
  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 + 1 = {ans}</p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
export default App;
Finaly, you can see this screen.

Advanced topics
In this article, I provided the quick introduction for Rust and Wasm. To use Wasm in production, you should think about the following topics.
- Logging
- Multi-Threading
- Exception handling
- Call JavaScript from Rust
- Call Rust structfrom JavaScript
- Testing
- Cross platform
Conclusion
In this article, I introduced followings through creating simple demo application.
- How to create a React app quickly with create-react-app.
- How to create a Wasm library with Rust.
- How to combile a React app with Wasm library.
Based on this, let's create a React app with Rust! Thank you for reading to the end.
