Import C++ and Rust
like JavaScript modules.
No bindings, no glue, no second build system. One import runs in the browser, on iOS and Android, in Node, on Workers, and as a WASI command.
Free and open source under the MIT license.
✓SAME CALL · FIVE RUNTIMES
Browser
Chrome · Firefox · Safari
Node.js
CJS or ESM
React Native
iOS + Android · JSI
CF Workers
Edge · same wasm
WASI
wasmtime 47+ · wasip3
FULL CAPABILITY MATRIX
26 capabilities · 9 platform detailsWrite it
The native API you expose to JavaScript
01Bindings you never writeImport a header and what it exposes is already bound — classes, inheritance, overloads, vectors, maps, enums. C++ exceptions arrive as JS exceptions, and there is no .delete() to remember: shared_ptr owns the objects.02Rust through the same importDeclare a crate, then import cargo:uuid. Structs become classes, Result throws, i64 crosses as BigInt, and you write no proc-macros. Two gaps to know up front: WASI has no Rust target yet, and the mt runtime needs a nightly toolchain.03Native code that calls you backemscripten::val hands C++ any JavaScript value, a callback included. Rust gets JsValue and JsFunction by identity, plus serde_json::Value when a deep copy is the safer boundary. Synchronous callbacks need a non-worker runtime.04TypeScript, generatedEvery header and Rust import gets a .d.ts, written under .cppjs/ and never into your source tree. One extends line wires the lot. Worker mode? Set dts: 'promise' and the types follow.05Hot reload for C++ & RustSave a header or a .rs file, watch the browser refresh. The edit-save-see loop web developers live in, now for native code.
Resolve it
How packages become a target-ready dependency graph
06The whole native graph, resolvedInstall the package you asked for. crossbind expands its transitive native dependencies, orders prerequisites before dependents and carries the same graph through library, app and WASI builds — no hand-sorted linker list.07The right artifact for this targetResolution filters every package across platform, architecture, st/mt runtime, release/debug build type and browser/edge/Node environment. Each dependency contributes only the variant the current target can consume.08Prebuilt first. Source when you ask.Published artifacts are the fast path. Pass --rebuild-deps with one package or the whole set and crossbind switches to its published source recipe without changing how the dependency is installed or imported.09Rebuild one dependency, not all of themName the source dependencies to rebuild and every other package stays on its cached or prebuilt artifact. Per-directory locks keep concurrent build commands from racing over the same dependency cache.
Build it
How source turns into every required target
10One command, the full build matrixcppjs build spans every valid target by default, with wasm64 kept opt-in. Platform, architecture, runtime, release/debug and runtime-environment filters compose — each accepts comma-separated values when a release needs only part of the matrix.11CMake, Autotools or your own runnerA package recipe can fetch or generate source, run CMake or configure + make, inject flags and environment variables, patch upstream files, copy assets or replace the build step entirely.12No toolchain to installemscripten, the Android NDK and wasi-sdk arrive together in one digest-pinned Docker image — nothing to set up, identical builds on any machine. iOS still goes through local Xcode, and Rust through your own cargo.13Nothing rebuilds twiceHeaders, bridges and libraries are content-hashed, so an unchanged one comes back as (cached) in the build log instead of recompiling. Cargo keeps its own incremental cache, and the Docker image is pulled once.14Only what you useDead-code elimination on top of -O3, plus platform-split downloads: you ship what you call and nothing else. Android is the exception — it links the whole library.15Override anythingTwenty documented override points: swap flags per target, patch sources, drop a dependency from the graph, or rebuild any prebuilt from source.
Run it
Where the same API executes
16One codebase, every platformWebAssembly in the browser, in Node and on the edge. Native machine code on iOS and Android through JSI. A wasip3 command for WASI. One API in front of a 30-target build matrix.17On-device by defaultYour C++ and Rust run where your user is. No backend to build or pay for, no request latency, no data leaving the device. It even works offline.18Off the main threadSIMD is on for every wasm build. runtime: 'mt' adds a warm pool of two pthread workers in the browser — iOS and Android get real OS threads — and useWorker: true moves the module off the main thread entirely, so even new returns a Promise. Production needs the two COOP/COEP headers; the dev server already sends them.19Big files, browser and NodeMulti-gigabyte files through OPFS: they persist across reloads and never need to fit in one upload. Mount straight from an <input type=file> — with useWorker: true, because OPFS is worker-only. On Node the same code reads real paths on your disk.
Ship it
What reaches apps, npm and the command line
20One graph, every artifactThe build emits browser, edge and Node .js + .wasm pairs; target-scoped headers and static libraries; and an iOS XCFramework. The WASI path produces the standalone WebAssembly command instead of JavaScript glue.21Libraries as npm dependenciesGDAL, OpenSSL, SQLite, GEOS, PROJ and 11 more, precompiled from the real upstream sources at pinned versions. Or publish your own — prebuilt binaries, C++ sources, a CMake project or a Rust crate. Either way the right platform variant installs itself.22Data files, auto-wiredCoordinate databases, format tables — declare them once. crossbind ships the files to every platform and wires the env vars for you.23Ship a CLI, no runtimeplatform: 'wasi' turns C++ into a single .wasm command with no JS glue. 68 classic tools already ship as <tool>-wasi npm commands — gdalinfo, sqlite3, openssl and friends.
Trust it
What you can inspect before depending on it
24A supply chain you can auditEvery upstream source is sha256-pinned and fetched over https. cppjs licenses writes a THIRD-PARTY-NOTICES file and a CycloneDX SBOM for exactly what you compiled, and --check fails the build on a missing or non-SPDX license.25You can see what it didThe build prints one timed line per target, cached steps included. cppjs config list --type all dumps the fully resolved configuration, and initNative({ logHandler, errorHandler }) routes native stdout and stderr wherever your app already logs.26Tested on the platforms it claimsThe sample apps are built and driven in CI, not merely compiled: Playwright across React, Vue, Svelte, vanilla and a Cloudflare Worker; wasmtime for the WASI commands; Maestro on an Android emulator and an iOS simulator. Core unit tests run against coverage thresholds on Linux, macOS and Windows.
Platform & tooling details
- Expo config plugin
- memory64 targets
- JSPI async (experimental)
- SWIG escape hatch
- Android arm64 + x86_64
- monorepo paths
- write your own bundler plugin
- CommonJS + ESM modules
- clean dependency cache command
PREBUILT LIBRARIES
View all 16 →Drop in real C++ libraries.
GDAL
The geospatial stack — 200+ raster and vector formats
geo · 3.13.2install →
OpenSSL
TLS and crypto, real https from a WASI command
crypto · 4.0.1install →
SQLite
The database, in the browser and on device
db · 3.53.4install →
PROJ
Coordinate transforms with the EPSG database
geo · 9.8.1install →
GEOS
Planar geometry — buffers, overlays, predicates
geo · 3.14.1install →
libTIFF
TIFF read/write with JPEG, ZSTD and LERC codecs
image · 4.7.2install →
libwebp
WebP encode and decode anywhere
image · 1.6.0install →
SpatiaLite
Spatial SQL on top of SQLite
geo · 5.1.0install →