stdweb: Rust bindings to the Web API with a JavaScript escape hatch
GitHub describes it as A standard library for the client-side Web. The repository metadata lists Rust as its primary language. The metadata lists the Apache-2.0 license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- stdweb is a Rust crate that binds Web APIs and lets you embed JavaScript directly in Rust. It targets client-side Web development, with a focus on interoperability and a design that predates and now coexists with wasm-bindgen.
- Who is it for?
- Adopt stdweb if you want a Rust-centric Web API layer with direct JavaScript embedding and a mature set of DOM bindings, especially if you already use cargo-web or need Emscripten targets. Do not adopt it if you prefer a wasm-bindgen-centric workflow or need the latest Web API coverage, since stdweb's changelog shows slower additions.
- 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?
- Probably not. The repository last received commits 32 months ago, on February 28, 2024.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Star history
- koute/stdweb3,452 ★ · -1
| Repository | 2026-08-13 | 2026-10-21 |
|---|---|---|
| koute/stdweb | 3.5k | 3.5k |
-1 Stars in the last 69 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What stdweb actually solves
stdweb is a Rust crate that provides bindings to Web APIs and a high degree of interoperability between Rust and JavaScript. It is for developers who want to write client-side Web applications in Rust without leaving the language for every DOM call or event handler. The README positions it as a building block for higher-level frameworks, not a full framework itself. It aims to put Rust in the driver's seat, meaning you can write a non-trivial Web application without touching JavaScript at all. But it also acknowledges that sometimes you need JavaScript, and it gives you a clean way to embed it. The core problem it solves is the impedance mismatch between Rust's static type system and the dynamic, untyped Web platform. It gives you typed access to the DOM, events, and other Web APIs, while letting you drop into raw JavaScript when the bindings are missing or too awkward.
The js! macro: embedding JavaScript with type-safe marshaling
The most distinctive feature is the js! macro, which lets you write JavaScript directly inside Rust code. The README shows a simple example: you pass a Rust string into a JavaScript alert call using @{message}, and the macro handles the marshaling. The macro also supports closures. You can pass a Rust closure into JavaScript, call it from there, and then explicitly call .drop() on it to clean up Rust-side resources. This explicit drop is a notable design choice: it forces you to manage the closure's lifetime manually, which is a departure from Rust's usual RAII patterns. The macro also works with serde-serializable structs. You derive Serialize, mark the type with js_serializable!, and then pass an instance into JavaScript as @{person}. The JavaScript side receives a plain object with the same field names. This is a pragmatic approach: you get type safety on the Rust side, but the JavaScript side sees ordinary values.
Web API bindings and event handling
Beyond the js! macro, stdweb exposes a set of Web APIs. The README shows a DOM example: querying a button, adding a click event listener, and hiding all anchors under a selector. The API follows JavaScript conventions closely, so document().query_selector returns an Option<Option<Element>>, reflecting both the possibility of no element and the possibility of an empty result. Event listeners are closures that receive a typed event, like ClickEvent. The changelog lists additions such as FullscreenChangeEvent, BeforeUnloadEvent, and UnloadEvent, plus methods like Document::fullscreen_enabled and InputElement::selection_start. This shows the API is actively extended, but the pace is modest. The design goal is to expose a full suite of Web APIs, but the reality is that you will hit gaps. The js! macro is the safety valve for those gaps, which is a reasonable trade-off.
Exporting Rust functions to JavaScript
stdweb also supports the reverse direction: exposing Rust functions to JavaScript. The README shows a #[js_export] attribute on a Rust function that returns a String. After compilation, you get a hasher.js file that can be required from Node.js or loaded via a script tag in a browser. In the browser, the generated code exposes a Rust.hasher promise, so you need to await it before calling the exported function. This is a different model from wasm-bindgen, which also exports functions but with a more integrated toolchain. stdweb's approach is simple: you compile to a single .js file that bundles the WebAssembly or asm.js module. For Parcel users, there is an experimental plugin that lets you import a Cargo.toml directly. This is a neat integration, but the word experimental in the README suggests it is not production-ready.
Getting started: cargo-web and target choices
To run the examples, you first install cargo-web with cargo install -f cargo-web. Then you go into an example directory and run cargo web start with one of three targets: wasm32-unknown-unknown, asmjs-unknown-emscripten, or wasm32-unknown-emscripten. The README is explicit that the native wasm32-unknown-unknown target requires cargo-web, while the Emscripten targets do not. This is a key constraint: if you want the modern WebAssembly backend without Emscripten, you must use cargo-web, which is a separate tool that manages the build and serves the app at localhost:8000. This is a heavier setup than a plain cargo build. The changelog mentions that minimum supported Rust version is 1.33.0 as of 0.4.16, so you need a reasonably recent toolchain. The dependency on cargo-web is a real barrier for some users, because it adds an extra layer of tooling that is not part of the standard Rust workflow.
Compatibility with wasm-bindgen and the ecosystem
The changelog reveals a significant development: starting with version 0.4.16, stdweb offers initial wasm-bindgen compatibility. This means you can use stdweb in projects that already use wasm-bindgen. That is a pragmatic move, because wasm-bindgen has become the de facto standard for Rust-to-JavaScript interop. The README also mentions that the runtime is compatible with newer versions of Emscripten. This dual compatibility is useful for teams that want to migrate gradually. However, the README does not detail how the two libraries interact. It is likely that stdweb's js! macro and wasm-bindgen's imports can coexist, but the exact mechanics are not documented here. This is a point you should verify before relying on it. The changelog also notes updates to unstable futures-related APIs, which suggests that stdweb is trying to keep pace with async Rust, but those APIs are explicitly unstable, so they are not for production use.
Limitations and when stdweb is the wrong tool
The most obvious limitation is that stdweb is not a full Web API implementation. The changelog shows a slow trickle of new methods and events, which means you will likely encounter missing APIs. The js! macro is a workaround, but it sacrifices type safety and can lead to runtime errors if you pass the wrong type. Another limitation is the dependency on cargo-web for the native wasm32-unknown-unknown target. If you prefer a standard cargo build with wasm-bindgen, stdweb adds unnecessary complexity. The explicit .drop() on closures is a footgun: if you forget to call it, you leak the closure on the Rust side. The README does not mention any automatic cleanup. Also, the README does not discuss error handling in the js! macro, such as what happens when JavaScript throws an exception. That is a significant gap for production use. If you need a mature, actively maintained Web API layer with a large community, stdweb may feel dated compared to wasm-bindgen.
Alternatives: wasm-bindgen and the difference in approach
The primary alternative is wasm-bindgen, which is mentioned in the changelog. wasm-bindgen takes a different approach: it uses a more declarative #[wasm_bindgen] attribute to import and export functions, and it generates the glue code automatically. It does not embed JavaScript directly in Rust; instead, you write JavaScript imports and Rust exports as separate items. This makes the interop more explicit and less magical than the js! macro. wasm-bindgen also has a richer ecosystem, with web-sys and js-sys crates that provide Web API bindings. Those bindings are generated from WebIDL, so they cover a much larger surface area than stdweb's hand-written bindings. However, wasm-bindgen does not support Emscripten targets like asmjs, so if you need asm.js for older browsers, stdweb has an advantage. The choice between the two comes down to whether you want the convenience of embedded JavaScript or the completeness of generated bindings.
Editorial conclusion
Adopt stdweb if you want a Rust-centric Web API layer with direct JavaScript embedding and a mature set of DOM bindings, especially if you already use cargo-web or need Emscripten targets. Do not adopt it if you prefer a wasm-bindgen-centric workflow or need the latest Web API coverage, since stdweb's changelog shows slower additions. Before committing, verify that your required Web APIs are present, test the js! macro with your data types, and confirm your target (wasm32-unknown-unknown, asmjs, or emscripten) works with your toolchain. stdweb is a viable choice, but its future depends on how well it tracks the rapidly evolving WebAssembly ecosystem.
Frequently asked questions
What is stdweb?
A Rust crate providing bindings to the Web APIs exposed by browsers, aiming at a high degree of interoperability between Rust and JavaScript. It exposes the full suite of browser APIs following JavaScript conventions, lets you embed JavaScript inside Rust, and supports serialising Rust structures for the other side.
How do I run JavaScript from Rust with stdweb?
With an inline macro that takes a JavaScript block, interpolates Rust values into it, and returns the result into Rust. Arbitrary Rust structures can cross the boundary too, given a serialisation derive and a macro that registers the type.
What happens to a Rust closure I pass into JavaScript?
You have to release it yourself. The example creates a closure, moves it into the JavaScript block, calls it twice, and then drops it explicitly, with a comment noting that dropping is necessary to clean up the closure on Rust's side.
Do I need cargo-web to run the stdweb examples?
Only for the native 32-bit WebAssembly target, which the documentation marks as requiring it. The two Emscripten targets, one producing asm.js and one producing WebAssembly through Emscripten, do not need it.
Is stdweb still maintained?
The last commit in this snapshot is dated 2024-02-28, the manifest version is 0.4.20, and the repository publishes no releases. The most recent changelog entries were about compatibility with newer WebAssembly and Emscripten tooling rather than new features.
Official sources
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.
[](https://hysenlabs.com/projects/koute-stdweb)Star history chart for your README
[](https://hysenlabs.com/projects/koute-stdweb)