TL;DR:
wasm-bindgenis a crate and CLI tool that generates the glue code needed for Rust WebAssembly modules to communicate with JavaScript. It lets you export Rust functions and structs to JS, import JS functions and browser APIs into Rust, and pass complex types (strings, objects, closures) across the Wasm boundary; which normally only supports integers and floats. Use it withwasm-packto build and publish WASM packages.
What Does wasm-bindgen Do?
It generates JavaScript bindings so Rust WASM functions can accept and return strings, objects, and closures; not just numbers. As of 2026, wasm-bindgen 0.2.x remains the standard, with ongoing work toward the Component Model (wit-bindgen) for the next generation of Wasm interop.
WebAssembly's native ABI only supports i32, i64, f32, f64. Every other type; strings, arrays, structs, callbacks; needs manual memory management across the JS/Wasm boundary. wasm-bindgen automates this entirely.
How Do You Export a Rust Function to JavaScript?
Add #[wasm_bindgen] to any public function or struct you want accessible from JS.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
#[wasm_bindgen]
pub struct Counter {
value: u32,
}
#[wasm_bindgen]
impl Counter {
pub fn new() -> Counter {
Counter { value: 0 }
}
pub fn increment(&mut self) { self.value += 1; }
pub fn get(&self) -> u32 { self.value }
}After building with wasm-pack build, call from JavaScript:
import init, { greet, Counter } from './pkg/my_crate.js';
await init();
console.log(greet("world")); // "Hello, world!"
const c = Counter.new();
c.increment();
console.log(c.get()); // 1How Do You Call JavaScript APIs From Rust?
Use #[wasm_bindgen] on extern "C" blocks to import JS functions and browser APIs.
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen]
pub fn show_alert(msg: &str) {
alert(msg);
log(&format!("Alert shown: {msg}"));
}web-sys and js-sys crates provide pre-generated bindings for the entire browser API; you rarely need to write extern "C" blocks manually.
How Do You Build a WASM Project?
Use wasm-pack; it compiles Rust to WASM, runs wasm-bindgen, and outputs an npm-compatible package.
# Install
cargo install wasm-pack
# Build (outputs to pkg/)
wasm-pack build --target web
# Build for Node.js
wasm-pack build --target nodejs
# Build for bundlers (webpack/vite)
wasm-pack build --target bundlerThe pkg/ directory contains the .wasm file, generated JS bindings, and a package.json ready for npm publish.
wasm-bindgen vs wit-bindgen in 2026
wasm-bindgen targets the browser JS ecosystem today; wit-bindgen targets the emerging WASM Component Model for cross-language interop beyond JS.
wasm-bindgen | wit-bindgen | |
|---|---|---|
| Target environment | Browser / Node.js (JS host) | Any WASM runtime (Wasmtime, WASI, etc.) |
| Binding language | JavaScript | Language-agnostic (WIT interface files) |
| Maturity | Stable, widely used | Stable spec, tooling still maturing |
| Maintainer | Rustwasm Working Group | Bytecode Alliance |
| Use with wasm-pack | ✅ Yes | ❌ No (separate toolchain) |
| Type richness | JS types + Web APIs | Full interface types across languages |
| Best for | Frontend Rust, Leptos, Yew, Dioxus | Serverless WASM, plugin systems, WASI |
If you are building a browser app or targeting npm with wasm-pack, use wasm-bindgen. If you are building a plugin system, a cloud WASM serverless function, or need Rust to interop with Go or Python WASM modules, look at wit-bindgen and the Component Model; it is the direction WASM is heading in 2026 and beyond.
Frequently Asked Questions
wasm-bindgen is the core binding mechanism. web-sys is a crate built on top of it that provides auto-generated bindings for all browser Web APIs (DOM, fetch, canvas, etc.).
Yes. Use js_sys::Array for JS arrays or Vec<u8> for byte arrays; wasm-bindgen handles the memory copying. For large data, use js_sys::Uint8Array with zero-copy views where possible.
For compute-heavy tasks (parsing, compression, cryptography), Rust WASM is typically 2–5× faster than equivalent JS. For DOM-heavy work, JS is faster because DOM calls cross the Wasm boundary.
No, but it is strongly recommended. wasm-pack orchestrates cargo build --target wasm32-unknown-unknown, runs wasm-bindgen-cli, and optionally runs wasm-opt for size optimization. Doing it manually is tedious and error-prone.
Yes. Use the wasm-bindgen-futures crate to bridge Rust futures and JavaScript Promises. spawn_local runs a future on the JS event loop, and JsFuture::from converts a JS Promise into a Rust Future.
Sources
- wasm-bindgen docs
- wasm-bindgen on crates.io
- Rust and WebAssembly Book
- Bytecode Alliance; Component Model
Related Glossary Terms
- wasm: WebAssembly overview and Rust's role in it
- leptos: Full-stack Rust framework that targets WASM
- yew: React-like Rust frontend framework built on wasm-bindgen
- dioxus: Cross-platform UI framework that also compiles to WASM
- tokio: Use
wasm-bindgen-futuresfor async Rust in WASM - async-await: Rust async primitives that work with
wasm-bindgen-futures - Topcoat: Experimental Rust web frameworks still rely on the same core Wasm interop primitives
Keep Reading
- Rust and WebAssembly Guide: wasm-bindgen is the entry point for all Rust WASM projects

