Quick Start - Running Rust code to WebAssembly (WASM) in Next.js

In comparison to several programming languages, I found Rust has the smallest size of compiled code to WASM. So I decided to write this article to help you get started with Rust code to WASM in Next.js. Running Wasm code in Next.js for every programming language has very unique implementation. So in this article, I'll show you how to run Rust code to WASM in Next.js.

Prerequisite

First of all, you need to have Rust installed with Cargo.

In order to compile Rust code to WASM, you need to install wasm-pack.

cargo install wasm-pack

Initialise a Rust project with Cargo.

cargo new --lib hello-wasm

This will create a new Rust project with a src/lib.rs file with the following directory structure:

hello-wasm/
├── Cargo.toml
├── src/
│   └── lib.rs

Then you need start Next.js project (with nodejs and npm installed) within different directory.

npx create-next-app@latest

and from src/app/page.tsx file, delete everything inside <main> tag and replace it with <h1>Hello World</h1>.

'use client'

export default function Home() {
  return <h1>Hello World</h1>
}

You can see the result of this code in the browser by running npm run dev command. Go to http://localhost:3000 in your browser. and you'll see Hello World text.

Write Simple Rust Code

write simple Rust code in src/lib.rs file.

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

This code is a simple function that takes two integers as input and returns their sum.

Compile Rust code to WebAssembly (WASM)

Before compiling, you need to add wasm-bindgen to your Cargo.toml file.

[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <[email protected]>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

To compile Rust code to WebAssembly (WASM), in your root directory, create a build.sh file and add this code:

wasm-pack build --target web

Here is the explanation of the build script:

  • wasm-pack build --target web: Compile the Rust code to WebAssembly (WASM) and save it as adder.wasm.

After you have created the build.sh file, you can give permission to execute it by typing chmod +x build.sh in your terminal. Then you can run it by typing ./build.sh in your terminal.

From the build process, you'll see some files in the pkg directory with the following structure:

pkg/
├── .gitignore
├── hello_wasm.d.ts
├── hello_wasm.js
├── hello_wasm_bg.wasm
├── hello_wasm_bg.wasm.d.ts
├── package.json

Copy all these files to the src/app/ directory of your Next.js project.

cp -r pkg <YOUR NEXTJS PROJECT>/src/app/

and you can see the directory structure of your project like this:

<YOUR NEXTJS PROJECT>/
├── src/
│   └── app/
│       ├── page.tsx
│       └── pkg/
│           ├── ...

Integrating WASM in Next.js

in the src/app/page.tsx file, you can write the code below:

"use client";

import { useEffect, useState } from "react";

const WebAssembly = {
  init: null as any, 
  greet: null as any,
  add: null as any,
};

interface NumberValue {
  a: number;
  b: number;
}

export default function Home() {
  const [numbers, setNumbers] = useState<NumberValue>({ a: 0, b: 0 });
  const [results, setResults] = useState(0);

  useEffect(() => {
    // dynamic imports

    async function loadWasm() {
      if (typeof window !== "undefined") {
        const { default: init, greet, add } = await import("./pkg/hello_wasm.js");
        WebAssembly.init = init;
        WebAssembly.greet = greet;
        WebAssembly.add = add;
      }
    }

    loadWasm();
  }, []);

  const handleAdd = () => {
    if (WebAssembly.add) {
      const res = WebAssembly.init().then(() => {
        return WebAssembly.add(numbers.a, numbers.b);
      }) 
      setResults(res);
    }
  };

  return (
  <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
    <h1 className="text-2xl font-bold text-center text-black">WASM Addition in Next.js</h1>
    <div className="p-8 bg-white rounded-lg shadow-md">
      <div className="mb-4 space-y-4">
        <input
          type="number"
          value={numbers.a}
          onChange={(e) => setNumbers({ ...numbers, a: parseInt(e.target.value) })}
          className="w-full px-4 py-2 text-black border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <input
          type="number"
          value={numbers.b}
          onChange={(e) => setNumbers({ ...numbers, b: parseInt(e.target.value) })}
          className="w-full px-4 py-2 text-black border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
      </div>
      <button
        onClick={handleAdd}
        className="w-full px-4 py-2 text-white bg-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
      >
        Add
      </button>
      <p className="mt-4 text-xl font-semibold text-center text-black">
        Result: <span className="text-blue-600">{results}</span>
      </p>
    </div>
  </div>
  
  );
}


repo link https://github.com/danirisdiandita/nextjs-wasm-quickstart

WASM Addition in Next.js