WebAssembly's Second-Class Problem Has a Solution

The WebAssembly Component Model proposes native browser API binding through WIT interfaces, eliminating the JavaScript glue layer responsible for a measured 45% overhead on DOM-heavy workloads.

WebAssembly's Second-Class Problem Has a Solution

WebAssembly is marketed as near-native performance on the web — but calling a single DOM API from Rust today requires more scaffolding JavaScript than most frameworks need to render an entire component tree.

Source

Why is WebAssembly a second-class language on the web?

By: Ryan Hunt (Mozilla)
Published: Feb 26 2026
Type: Technical deep-dive

This is one of the clearest articulations of why WebAssembly adoption stays concentrated in large companies and hasn’t reached average developers — and the Component Model section makes a concrete case for how that changes.

The Glue Code Tax and the Component Model

WebAssembly can’t access Web APIs directly. Every call from a Wasm module to the browser — a DOM mutation, a console log, a fetch — must pass through a JavaScript glue function that re-encodes data between the two memory models, allocates JavaScript objects, manages shared memory lifetimes, and hands the call off to the actual API. A console.log from a Rust program compiled to WebAssembly requires roughly 40 lines of JavaScript scaffolding. Tools like wasm-bindgen and Emscripten generate this code automatically, but they are language-specific, require a third-party toolchain distribution that standard upstream compilers don’t produce, and impose a runtime cost at every API boundary.

That cost was measured directly in 2020 using the TodoMVC benchmark on an experimental Rust framework. Stripping out the JavaScript glue layer reduced the time to apply DOM changes by 45%. That’s not marginal noise — it means WebAssembly today imposes roughly a 2x overhead on DOM-heavy workloads relative to what direct binding would cost. DOM operations are already expensive; paying a 2x tax on top keeps WebAssembly uncompetitive for UI work and limits it to compute-heavy tasks where the calculation time dwarfs the boundary overhead.

The structural reason this exists is that WebAssembly arrived after JavaScript. JavaScript co-evolved with the web platform and has direct access to its APIs. WebAssembly was designed to slot in below JavaScript, using JavaScript as the intermediary for anything platform-facing. This layering made sense initially but creates what Ryan Hunt describes as “the wall”: while learning JavaScript has a smooth complexity curve, getting started with WebAssembly requires immediately mastering glue code infrastructure, toolchain distributions, and memory encoding patterns. The break-even point shifts to large projects with enough resources to absorb that learning cost, which keeps the benefits of WebAssembly concentrated among large companies.

The proposed fix is the WebAssembly Component Model, a standards-track proposal in development since 2021. A Component is a self-contained binary that declares its API surface using an IDL called WIT (WebAssembly Interface Types). The browser binds the declared interfaces natively — no JavaScript involved. The load path becomes a single script tag: <script type="module" src="component.wasm"></script>. For hybrid apps where some code is JavaScript and some is WebAssembly, the same mechanism handles cross-language interoperability: JavaScript imports a .wasm component and calls its exports using normal JavaScript types, with the runtime handling type conversion at the boundary.

The WIT interface model matters for documentation as well as performance. Today, web platform docs are written for JavaScript developers. A Rust or C++ developer must mentally translate every API description into the types and patterns their toolchain exposes. If component interfaces are derived from the same WebIDL that browsers use natively, the translation layer disappears — language-specific docs can be generated from a single source rather than being manually written and maintained per toolchain.

Full native browser support isn’t there yet. Mozilla is actively implementing the esm-integration proposal (which enables importing .wasm files directly from JavaScript modules) and working with the WebAssembly CG on the component specification. Tooling exists today — Jco for browser use, Wasmtime for the command line — but Component Model support built into browsers rather than polyfilled is still ahead.

The common misread is that this is about replacing JavaScript. It isn’t. The target is making WebAssembly a peer: a language you can use on the web without JavaScript expertise as a prerequisite, and without paying the glue overhead that currently makes it the wrong choice for anything other than isolated compute.

What I want to remember

Remember: The WebAssembly Component Model (WIT interfaces + native browser binding) is the fix for the JS glue overhead — currently a measured 45% on DOM work — that keeps WebAssembly a power-user tool rather than a general web language.