CLI tool
wasm-bindgen/wasm-bindgen avatar
wasm-bindgen/wasm-bindgen

wasm-bindgen: Rust-to-JavaScript bindings without hand-written glue

Facilitating high-level interactions between Wasm modules and JavaScript

9,165 stars1,249 forksRustApache-2.0

At a glance

What is it?
wasm-bindgen generates the JavaScript shims that let Rust compiled to WebAssembly call into the browser and Node, and lets JavaScript call back into Rust. It is the binding layer under most of the Rust wasm ecosystem, and it comes with a CLI whose version must match the crate.
Who is it for?
Adopt wasm-bindgen if you are writing Rust that must call DOM APIs, fetch, or any JavaScript function, and you want generated bindings rather than a hand-maintained glue file. Skip it if your wasm module only exchanges numbers and memory buffers with the host, since the binding layer earns its cost only when you cross the Rust/JavaScript boundary often.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The glue problem wasm-bindgen exists to remove

A WebAssembly module on its own can only take and return numbers. Anything richer, a string, a DOM node, a JavaScript object, requires the host to place data in linear memory and pass a pointer and a length across the boundary. Writing that by hand for every function is tedious and error-prone, and it is the same code every Rust wasm project would otherwise rewrite.

wasm-bindgen is aimed at Rust developers who want to call JavaScript APIs from Rust and expose Rust functions to JavaScript without maintaining that layer themselves. The README frames the goal as "Facilitating high-level interactions between Wasm modules and JavaScript." In practice that means you annotate Rust items with attribute macros and a CLI tool generates the JavaScript module that wires them up.

The project also supplies companion crates that most users end up depending on: js-sys for ECMAScript built-ins, web-sys for Web IDL interfaces such as the DOM, and wasm-bindgen-futures for bridging Rust futures to JavaScript promises. The repository layout reflects this: the root crate holds the macro and runtime, while crates/ contains js-sys, web-sys, futures, cli and test.

How the macro and the CLI split the work

There are two halves, and understanding the split explains most of the failure modes people hit.

The Rust half is the #[wasm_bindgen] attribute macro. On an extern "C" block it marks imported JavaScript functions; on a pub fn it marks a Rust function for export. The macro records what it saw, and the compiler emits a custom section into the resulting wasm binary describing the imports and exports, their types, and how strings and other non-numeric values should be converted. The README's example shows both directions in one file: an extern block declaring alert, and a pub fn greet that calls it.

The JavaScript half is the wasm-bindgen CLI. It reads that custom section from the .wasm file and writes a JavaScript module plus possibly a second wasm file. The README describes the output as an ECMAScript module you import directly: import { greet } from "./hello_world". The generated code is what actually converts a JS string into a pointer and length, calls the wasm export, and decodes the result.

Because the CLI reads metadata that the macro wrote, the two are version-locked. The Cargo.toml pins wasm-bindgen-macro and wasm-bindgen-shared to =0.2.128, an exact version rather than a caret range. A CLI from a different release may not understand the schema in the binary.

Installing wasm-bindgen-cli and running a first binding

The README gives three install routes for the CLI. The simplest is cargo install:

bash
cargo install wasm-bindgen-cli

If cargo-binstall is present, the README says pre-built artifacts can be fetched instead:

bash
cargo binstall wasm-bindgen-cli

The third route is downloading a binary from the GitHub release page. Note the MSRV split in the README: libraries released on crates.io target Rust 1.77, while CLI tools and their support libraries target 1.86. If your toolchain is older than 1.86, the install command above will fail even though the library crate would compile.

For the Rust side, add the crate to your project. The README's example uses the prelude:

rust
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

Build the crate for the wasm target, then run the CLI over the produced .wasm file to generate the JavaScript module. The README does not spell out the CLI invocation in the excerpt shown here; it points to the guide at wasm-bindgen.github.io for the full workflow. What you should end up with is a JS file exporting greet, which you call from JavaScript:

js
import { greet } from "./hello_world";

greet("World!");

The repository also ships a working example under examples/hello_world/, and examples/README.md documents how the example set is built and run. If you want to see the generated output before writing your own crate, that directory is the place to look.

The version-mismatch failure and what it looks like

The most common way a wasm-bindgen build breaks is a CLI that does not match the crate. The error surfaces at the CLI step, after a successful cargo build, which makes it feel like a toolchain problem when it is a version problem. The exact-version pins in Cargo.toml (wasm-bindgen-macro and wasm-bindgen-shared both at =0.2.128) exist for this reason.

This is a real cost of the design. The macro and the CLI communicate through a binary schema embedded in the wasm file, and that schema is not a stable public interface. Upgrading the crate without upgrading the CLI, or the reverse, is unsupported. In a CI pipeline it means the CLI install step cannot be cached indefinitely against a floating version; it has to track the lockfile.

There is a second, quieter limitation. The README describes the feature set as lightweight, and quotes the design goal: "Only pay for what you use." That is true of generated glue, not of the web-sys crate. Enabling web-sys features pulls in bindings for the interfaces you list, and the feature list is long. A project that turns on a broad set of web-sys features gets a larger generated module than one that names only the interfaces it touches. The README does not document a size budget or a way to audit what a feature set costs.

When wasm-bindgen is the wrong layer

If your wasm module is a compute kernel, a codec, a physics step, or anything that exchanges numbers and slices of linear memory with a host that already knows how to read them, wasm-bindgen adds a layer you do not need. The macro attributes, the custom section, the CLI pass and the generated module are all overhead when the boundary is a pointer and a length.

The other case is when your host is not JavaScript. The binding model here is specifically about JavaScript and Web IDL interfaces. A wasm module embedded in a Rust host, a Python host, or a WASI runtime does not benefit from a generator whose output is an ECMAScript module and whose type vocabulary is js-sys and web-sys.

It is also worth being clear that wasm-bindgen does not build your project. It generates bindings for a .wasm file you already produced. The build orchestration, dev server, and packaging story live elsewhere, which is why the ecosystem pairs it with other tools.

wasm-bindgen compared with wasm-pack and Emscripten

The two comparisons people reach for are wasm-pack and Emscripten, and they sit at different levels.

wasm-pack is a workflow tool. It invokes the Rust build, runs the wasm-bindgen CLI, and packages the result for npm, including the JavaScript wrapper and TypeScript definitions. wasm-bindgen is the binding generator that wasm-pack calls. Choosing wasm-pack does not replace wasm-bindgen; it wraps it and pins a compatible CLI version for you, which sidesteps the mismatch described above at the cost of one more tool in the chain. If you want the generated module and nothing else, calling the CLI directly is the smaller dependency.

Emscripten is a different proposition: it compiles C and C++ to WebAssembly and ships its own JavaScript runtime, memory model, and emulation of POSIX-ish APIs. wasm-bindgen starts from Rust and generates per-function glue rather than a general runtime. The README also notes the project is designed with the Web IDL bindings proposal in mind, with the stated aim that eventually there will be no JavaScript shims between Rust-generated wasm functions and native DOM methods. That is a direction, not a current capability, and the README's own phrasing is "promising to unlock" rather than a claim about today.

Maintenance, MSRV drift and licensing

The repository is not archived and the last push was on 2026-09-18, three days before this writing. Releases are frequent: 0.2.128 on 2026-09-05, 0.2.127 on 2026-08-08, 0.2.126 on 2026-06-24. The version numbering stays in the 0.2.x line, so semver compatibility expectations should be read accordingly.

The upgrade cost is dominated by the MSRV policy. The README states the project aims for a two-year MSRV window for libraries, with a shorter window for the CLI, and warns that MSRV changes may land in patch versions. The MSRV history table shows the library floor moving from 1.57 to 1.71 to 1.77 between 2024-08-13 and 2026-04-10, while the CLI floor went from 1.76 to 1.82 to 1.86. A patch release can therefore raise the minimum Rust version your build needs, and the CHANGELOG is where that is logged. Teams pinned to an older toolchain should read the CHANGELOG before bumping.

Licensing is dual: Apache-2.0 or MIT, at your option, with both LICENSE-APACHE and LICENSE-MIT present at the repository root. Contributions are accepted under the same dual terms unless stated otherwise. That is a permissive arrangement common in the Rust ecosystem, but whether it fits your distribution model is a question for your own legal review, not something the README settles.

Editorial conclusion

Adopt wasm-bindgen if you are writing Rust that must call DOM APIs, fetch, or any JavaScript function, and you want generated bindings rather than a hand-maintained glue file. Skip it if your wasm module only exchanges numbers and memory buffers with the host, since the binding layer earns its cost only when you cross the Rust/JavaScript boundary often. Before committing, verify that your installed wasm-bindgen-cli version exactly matches the wasm-bindgen crate version in Cargo.lock, and check the MSRV table against your toolchain: libraries need Rust 1.77, CLI tools need 1.86.

Frequently asked questions

What is wasm-bindgen?

It is a Rust crate plus a CLI that generates the JavaScript glue between a WebAssembly module and JavaScript. You annotate Rust items with #[wasm_bindgen], and the CLI reads metadata the macro embedded in the wasm binary to produce an ECMAScript module.

What does wasm-bindgen do?

It lets Rust import JavaScript functions and export Rust functions to JavaScript without hand-written glue. The README example declares window.alert as an import and exports a greet function that calls it, and the generated module is imported from JavaScript as an ES module.

How do I install wasm-bindgen?

The README gives three routes for the CLI: cargo install wasm-bindgen-cli, cargo binstall wasm-bindgen-cli if cargo-binstall is present, or downloading a binary from the release page. CLI tools have an MSRV of Rust 1.86.

How do I use wasm-bindgen?

Add the crate, annotate imports inside an extern "C" block and exports on pub fn items, build for the wasm target, then run the CLI over the produced .wasm file. The README points to the guide for the full workflow and ships a hello_world example in the repository.

What is the difference between wasm-bindgen and wasm-pack?

wasm-pack is a workflow tool that builds the Rust crate, runs the wasm-bindgen CLI and packages the output for npm. wasm-bindgen is the binding generator that wasm-pack invokes, so wasm-pack wraps it rather than replacing it.

What is a wasm-bindgen alternative?

Emscripten is the usual comparison, but it compiles C and C++ and ships its own JavaScript runtime and memory model. wasm-bindgen starts from Rust and generates per-function glue, and the README notes the design anticipates the Web IDL bindings proposal.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. wasm-bindgen/wasm-bindgen on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wasm-bindgen-wasm-bindgen.svg)](https://hysenlabs.com/projects/wasm-bindgen-wasm-bindgen)