Rust and WebAssembly
npm install @kadal/deck-wasmregisterLazyRenderer("wasm", () => import("@kadal/deck-wasm").then((m) => m.wasmRenderer));A WebAssembly module can’t reach the DOM, so every WASM UI framework ships a JavaScript shim — and the shim is the only thing a renderer can hold. This renderer drives an agreement rather than a framework:
export interface WasmIslandModule { default?: () => Promise<unknown>; // the initialiser wasm-bindgen generates deckMount(host: Element, props: IslandProps): WasmIslandHandle | Promise<WasmIslandHandle>;}
export interface WasmIslandHandle { update?(props: IslandProps): void; destroy(): void; free?(): void; // wasm-bindgen's, called after destroy when present}Rust (Leptos, Dioxus, Yew)
Section titled “Rust (Leptos, Dioxus, Yew)”In Rust it is one attribute:
#[wasm_bindgen(js_name = deckMount)]pub fn deck_mount(host: HtmlElement, props: JsValue) -> TickerHandle { /* … */ }Build with wasm-bindgen --target web and load the generated module through wasmBindgenModule:
import { wasmBindgenModule } from "@kadal/deck-wasm";
<Island framework="wasm" load={() => import("./ticker/pkg/ticker.js").then(wasmBindgenModule)} props={{ step }}/>Why wasmBindgenModule: a wasm-bindgen module’s default export is its initialiser, not the
component. <Island load> unwraps default for every JavaScript framework; here that would call the
initialiser and then hand raw WebAssembly a DOM element. The helper says what the module is.
Two things in a Leptos crate driven from outside a router: use ArcRwSignal, not RwSignal (the
signals outlive the mount closure), and keep the unmount handle as a Box<dyn Any> (a
#[wasm_bindgen] struct can’t carry a generic; dropping the box still runs the real Drop). An
optimised Leptos island in the showcase is 107 kB.
Props cross a wall
Section titled “Props cross a wall”Only JSON-shaped values cross into WebAssembly. Unserialisable props are dropped with a warning rather
than thrown — one bad prop shouldn’t take the slide down. free() is called after destroy(), because
wasm-bindgen’s structs are pointers into linear memory the JavaScript collector can’t see.
This is the only renderer that can honestly implement owns(): a module exporting deckMount said so
on purpose. The showcase also carries thirty lines of hand-written WebAssembly text, assembled to 195
bytes, using the same agreement.