Introduction
crossbind compiles C++ and Rust into WebAssembly, Node-API addons and native iOS and Android libraries, then hands the result to JavaScript as an ordinary module; the same sources also build into executables that need no JavaScript at all. 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. In a browser the module runs in a Web Worker by default, so calls return promises.
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 and C++ libraries ship prebuilt as @crossbind/port-* - GDAL, OpenSSL, SQLite, GEOS, PROJ and more, compiled from the real upstream sources at pinned versions. Install the variant for your platform and import its header directly; the upstream library is precompiled, and only your own code, the bindings and the final link go through the build toolchain.
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 crossbind ports; every library is listed under Libraries.
The same one-line import reaches two registries: a Rust crate from crates.io with cargo:, and a C or C++ package from ConanCenter with conan:.
Where the output runs
| Platform | What is produced |
|---|---|
| Browser | WebAssembly and a JavaScript loader, through your bundler |
| Node.js | WebAssembly, or a native Node-API addon |
| Edge | WebAssembly for Cloudflare Workers, or a wasi:http component |
| Electron | The Node-API addon, loaded in the main process |
| React Native | Native iOS and Android code over JSI - no wasm |
| Executables | Native programs for macOS, Linux and Windows, or a WASI command |
The JavaScript you write does not change between the JavaScript platforms; the build target does. Executables and wasi:http components run with no JavaScript host.
What ends up in your bundle
Only the code reachable from the headers you imported is linked in: dead-code elimination on top of -O3 is on by default, so pulling two functions out of a large library costs those functions and what they reach, not the whole library.
Performance and size has the rest: what you can tune and what to leave alone.
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.
Why crossbind
The header import is the front door. Underneath it, crossbind is the native library distribution layer for JavaScript runtimes: versioned packages, target-specific artifacts, transitive dependencies, build recipes and runtime adapters.
- Target-aware distribution. A package family carries WebAssembly, iOS, Android and WASI variants, and each build consumes only the artifact valid for its platform, architecture, runtime and build type.
- Native dependency resolution. Package manifests and crossbind configs carry the transitive native graph, so prerequisites are built and linked in the right order.
- A reproducible package contract. Port recipes record upstream versions, source integrity, licenses, dependencies and target-specific artifacts.
- No hand-written binding glue. Public functions, classes, methods, enums, containers and the supported standard-library types are generated from the header you already own.
- C++ and Rust. Import local native sources, a
cargo:crate or a reusable native package through the same project. - First-party integrations and ports. Bundler plugins give incremental builds, and version-pinned ports make GDAL, SQLite, OpenSSL, GEOS, PROJ and more available without rebuilding upstream.
Limits worth knowing
- Browser OPFS persistence requires
useWorker: true: crossbind's OPFS integration relies on synchronous access handles, which browsers expose only in worker scope. - Browser multithread builds (
runtime: 'mt') need COOP/COEP headers in production, and the pthread pool defaults to two workers; the size and the exhaustion policy are configurable. - Edge runtimes such as Cloudflare Workers run single-threaded with an in-memory filesystem: no workers, no OPFS.
- Android builds link whole archives; dead-code elimination is not implemented there.
- iOS and macOS builds need a local Xcode, iOS also CocoaPods, and Rust bindings for them a local cargo; WASI commands need wasmtime on PATH.
- There is no performance claim: a hand-written loop in JIT JavaScript can match the same algorithm in Wasm. The value is the libraries you would otherwise rewrite.
- The 2.0 line is a beta; prereleases carry no compatibility guarantee until the first stable train.
Every option and default is in Configuration, Runtime API and CLI.
Where to next
- Quick start - from an empty directory to a running app.
- Tutorial - short steps that run in your browser, with nothing to install.
- C++ - the rules your headers have to follow, and the type table.
- Troubleshooting - the errors people hit most, with the standard fix for each.