Introduction
crossbind compiles C++ and Rust into WebAssembly, native iOS and Android libraries, and WASI commands - then hands the result to JavaScript as an ordinary module. You import a header; the bindings are generated from it.
There is no glue code to write and no second build system to run. The header you already have is the interface: whatever it declares - functions, classes, inheritance, overloads, vectors, maps, enums - shows up on the JavaScript side under the same names.
One import, no glue
Put your C++ under src/native, then import the header from JavaScript. initNative() boots the runtime once; after it resolves, every symbol the header exposes is callable.
The same shape works for Rust: an app-local .rs file, or a crates.io crate imported through the cargo: scheme. See Rust.
Libraries you do not have to build
16 C++ libraries ship prebuilt as @cpp.js/package-* - GDAL, OpenSSL, SQLite, GEOS, PROJ and more, compiled from the real upstream sources at pinned versions. Install one and import its header directly; nothing is compiled on your machine.
One initNative() covers every module on the page: each imported header registers its bindings, the call boots the runtime and resolves all of them together. Full details in Packages.
Where the output runs
| Target | What is produced | Guide |
|---|---|---|
| Browser | WebAssembly + a JS loader | Runtimes |
| Node.js | WebAssembly, host filesystem access | Runtimes |
| Cloudflare Workers / edge | WebAssembly, single-threaded, in-memory fs | Runtimes |
| iOS and Android | Native machine code over JSI - no wasm | Runtimes |
WASI (wasm32-wasip3) | One .wasm command, no JS host | WASI commands |
The JavaScript you write does not change between them. The build target does.
What ends up in your bundle
Only the code reachable from the headers you imported is linked in. Dead-code elimination and LTO are on by default, so pulling two functions out of a large library costs two functions, not the library.
Two ways to use it
- Write C++ (or Rust) yourself. Your own sources under
src/native, imported by header. Best when the work belongs on the native side - a whole pipeline in one call, no boundary crossing per step. - Drive a prebuilt library from JavaScript. Import the package header and call into it directly. Quickest to wire up, and the way most people start.
Both paths use the same runtime and the same initNative() call, and they mix freely in one project.
Where to next
- Quick start - from an empty directory to a running app.
- Bundlers - Vite, Webpack, Rspack, Rollup, Metro, or no bundler at all.
- C++ bindings - the rules your headers have to follow, and the type table.
- Troubleshooting - the errors people hit most, with the standard fix for each.