Using WASM With SvelteKit

Brace yourselves, this will be a doozy…

First all install wasm-pack which will compile our Rust code into Web Assembly, then install vite-plugin-wasm-pack & fs using as a dev dependency to your SvelteKit project. now create a new Rust library which will be used for WASM run this command at the root of your SvelteKit project

cargo new wasm-test --lib

Last thing to install is wasm-bindgen, which is a Rust library used to interact with Javascript code.

Add the following to your Cargo.toml file to install it:

# needed for target wasm type
[lib]
crate-type = ["cdylib"]

# deps
[dependencies]
wasm-bindgen = "0.2.63"

Now write some Rust code to compile it to WASM, in src/lib.rs write:

use wasm_bindgen::prelude::*;

// import Javascript's alert method to Rust
#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

// export Rust function greet to be used in JS/TS, the same function signature will be used in JS/TS
#[wasm_bindgen]
pub fn greet(str: &str) {
    alert(&format!("Hello, {}!", str));
}

Now the most annoying part, getting SvelteKit to use that WASM code, notice we haven’t built our Rust into WASM yet, we will do that before each run for our SvelteKit project and before the build, as you guessed we need to do some magic in the following configuration files.

package.json: add these scripts

"modulize-wasm": "node ./wasm-test/modulize.js",
"wasm": "wasm-pack build ./wasm-test--target web && npm run modulize-wasm",

And run wasm before dev & build i.e.

"dev": "npm run wasm && vite dev",
"build": "npm run wasm && vite build",

vite.config.ts Vite configurations are needed, since vite doesn’t allow importing a module from outside Vite’s working directory

// import the WASM packer that we installed earlier
import wasmPack from "vite-plugin-wasm-pack";

// add this to plugins
wasmPack("./wasm-test");

Finally, not really, but whatever, anyway, the package generated by wasm-pack is using commonjs, even when web is present in building target, so we need to hack the universe to get it working.

create a JS file in the Rust library, ./wasm-test/modulize.js, yes this is the same file you saw in package.json scripts, what this fine gentleman does is it adds the module thingy to the WASM target code, inside that file put:

import { readFileSync, writeFileSync, unlinkSync } from "fs";

const dirName = "./wasm-test/pkg/"; // change this to match your Rust library's name

const content = readFileSync(dirName + "package.json");

const packageJSON = JSON.parse(String(content));
packageJSON["type"] = "module";

writeFileSync(dirName + "package.json", JSON.stringify(packageJSON));

Now we get to say finally, in any used SvelteKit component import the WASM code


<script lang="ts">
  import init, { greet } from "wasm";
  // we need onMount to run init
  import { onMount } from "svelte";

  onMount(async () => {
    await init(); // init initializes memory addresses needed by WASM and that will be used by JS/TS
  })
</script>

<div>
  <button on:click={() => {greet("Eloi")}}>Click Me</button>
</div>

In conclusion we got Rust/WASM code to work with SvelteKit/TS, hopefully I’ll write something useful, that require performance more than a greeter function, something like canvas, enjoy WASM with SvelteKit.