crossbind
GitHub
GUIDE · GETTING STARTED · RUNTIMES

Runtimes

The same JavaScript runs on every runtime; what changes is the build target and which runtime features exist there. This page is the map: what to build, how to load it, and what is unavailable before you find out the hard way.

What each runtime supports

RuntimeOutputThreads (mt)useWorkerPersistent storage
Browserwasm + JS loaderyes, needs COOP/COEPyesOPFS, with useWorker: true
Node.jswasm + JS loaderyesno (n/a)the host filesystem
Cloudflare Workers / edgewasm + JS loadernonono - external store only
React Nativenative .a / xcframework over JSIyes, no headers neededn/athe app sandbox
WASIone .wasm commandno (single-threaded for now)n/apreopened host dirs

Threading and useWorker are two independent axes, and the difference bites often enough to have its own page: Threading and workers.

Target flags

A build target is a {platform, arch, runtime, runtimeEnv, buildType} tuple. With a bundler plugin the tuple is chosen for you; from the CLI you filter it with flags.

FlagValues
-p platformwasm, wasi, android, ios
-a archwasm32, wasm64, arm64-v8a, x86_64, iphoneos, iphonesimulator
-r runtimest, mt
-e runtimeEnvbrowser, node, edge
-b buildTyperelease, debug
shell
cppjs build -p wasm -a wasm32 -r st -e browser -b release

Browser

The default. Install the plugin for your bundler, import the header, call initNative(). Two browser-only options matter:

src/main.js
import { initNative } from './native/native.h';
 
const m = await initNative({
useWorker: true, "color:#78829a">// wasm runs in a Web Worker; required for OPFS
fs: { opfs: true }, "color:#78829a">// default in the browser
});

With useWorker: true every call crosses a worker boundary and therefore returns a promise - including construction (await new X()). Files then live in the filesystem you mounted.

Node.js

Build with -e node and import the generated loader. m.FS reads and writes the real host filesystem, so there is no /opfs versus /memfs distinction.

package.json
{
"scripts": {
"build": "cppjs build -p wasm -e node -r st"
},
"devDependencies": {
"cpp.js": "^2.0.0-beta"
}
}
src/index.mjs
import initNative from '../dist/myapp-wasm-wasm32-st-release.node.js';
 
const { MySampleClass } = await initNative();
console.log(MySampleClass.sample());

CommonJS works the same way with require(...). Environment variables for the wasm process go through init's env option, where _CPPJS_DATA_PATH_ expands to the runtime data path.

Cloudflare Workers and the edge

Edge runtimes are V8 isolates with no Web Worker API, so useWorker and runtime: 'mt' are both unavailable - build -e edge -r st. Bundle the wasm and hand it to initNative directly, which avoids a network round-trip at cold start.

index.js
import initNative from './dist/myapp-wasm-wasm32-st-release.edge.js';
import wasmContent from './dist/myapp-wasm-wasm32-st-release.edge.wasm';
 
const { MySampleClass } = await initNative({ getWasmFunction: () => wasmContent });
 
export default {
async fetch(request, env, ctx) {
return new Response(MySampleClass.sample());
},
};
No persistence
The edge filesystem is in-memory and lives for one invocation. If you need durable bytes, read them from R2/KV/S3 in JavaScript and write them in with m.FS.writeFile(...).

React Native and Expo

Mobile compiles to real native code and reaches JavaScript through JSI - no wasm, no SharedArrayBuffer, no COOP/COEP. runtime: 'mt' works with no host configuration. Wire Metro as shown in Bundlers, then call into the header from a component:

src/App.tsx
import { useState, useEffect } from 'react';
import { initNative, MySampleClass } from './native/MySampleClass.h';
 
export default function App() {
const [message, setMessage] = useState('compiling ...');
 
useEffect(() => {
initNative().then(() => setMessage(MySampleClass.sample()));
}, []);
 
return <Text>Response from C++ : {message}</Text>;
}

Expo

Expo Go cannot load custom native code, so switch to a development build first, then add the config plugin - it wires the native build during expo prebuild.

shell
npx expo prebuild
npx expo customize metro.config.js
app.json
{
"expo": {
"plugins": ["@cpp.js/plugin-react-native"]
}
}

iOS also needs pod install inside ios/ before the first npm run ios.

WASI

With -p wasi there is no JavaScript host at all: the output is a single wasm32-wasip3 command you run under wasmtime. Prebuilt CLI tools ship the same way as npm packages. Full details in WASI commands.

shell
cppjs build -p wasi -b release
wasmtime run --dir=. dist/myapp-wasi-wasm32-st-release.wasm input.txt
Type to search every guide page and section.
↑↓ navigate↵ openesc close