crossbind
GitHub
GUIDE · GETTING STARTED · BUNDLERS

Bundlers

A bundler plugin is what makes the header import work: it resolves .h imports, generates the bridge, compiles on demand in dev, and emits the wasm next to your bundle. Pick the one matching your build, or skip the plugin entirely and compile straight from the CLI.

Which plugin

Build toolInstallRegistered in
Vite@cpp.js/plugin-vitevite.config.js
Rollup@cpp.js/plugin-rolluprollup.config.js
Webpack@cpp.js/plugin-webpack + @cpp.js/plugin-webpack-loaderwebpack.config.js
Rspack@cpp.js/plugin-webpack + @cpp.js/plugin-webpack-loaderrspack.config.mjs
Metro (React Native)@cpp.js/plugin-react-native + @cpp.js/plugin-metrometro.config.js
Nonecpp.jsa build script - see standalone

Whichever you pick, the project also needs a cppjs.config.js at its root. The minimal one only sets paths.config; see Configuration.

Vite

shell
npm install -D @cpp.js/plugin-vite
vite.config.js
import { defineConfig } from 'vite';
import viteCppjsPlugin from '@cpp.js/plugin-vite';
 
export default defineConfig({
plugins: [viteCppjsPlugin()],
});

The Vite plugin injects COOP/COEP headers for vite dev and vite preview, so multithreaded builds work in development with no extra setup. Production hosting is your own - see Threading.

Rollup

The Rollup plugin is the kernel the Vite one wraps. Use it directly when you build with plain Rollup; on Vite, use the Vite plugin instead.

shell
npm install -D @cpp.js/plugin-rollup
rollup.config.js
import rollupCppjsPlugin from '@cpp.js/plugin-rollup';
 
export default {
plugins: [rollupCppjsPlugin()],
};

Webpack

shell
npm install -D @cpp.js/plugin-webpack @cpp.js/plugin-webpack-loader
webpack.config.js
const CppjsWebpackPlugin = require('@cpp.js/plugin-webpack');
 
const cppjsWebpackPlugin = new CppjsWebpackPlugin();
 
module.exports = {
plugins: [cppjsWebpackPlugin],
module: {
rules: [cppjsWebpackPlugin.getRule()],
},
devServer: cppjsWebpackPlugin.getDevServerConfig(),
};

getDevServerConfig() is what carries the COOP/COEP headers in dev; keep it if you build multithreaded.

Rspack

Rspack uses the same two packages as Webpack, registered in ESM form.

rspack.config.mjs
import CppjsWebpackPlugin from '@cpp.js/plugin-webpack';
 
const cppjsWebpackPlugin = new CppjsWebpackPlugin();
 
export default defineConfig({
module: {
rules: [cppjsWebpackPlugin.getRule()],
},
plugins: [cppjsWebpackPlugin],
devServer: cppjsWebpackPlugin.getDevServerConfig(),
});

Metro (React Native)

React Native compiles to native libraries rather than wasm, so it needs the autolinked runtime packages plus the Metro plugin that runs while bundling.

shell
npm install @cpp.js/plugin-react-native @cpp.js/plugin-react-native-ios-helper
npm install -D @cpp.js/plugin-metro
metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const CppjsMetroPlugin = require('@cpp.js/plugin-metro/metro-plugin.cjs');
 
const config = {
...CppjsMetroPlugin(getDefaultConfig(__dirname)),
};
 
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
Do not add cpp.js itself
@cpp.js/plugin-react-native brings the toolchain it was built against. A second pin in your package.json can drift from it.

Expo needs one more step (expo prebuild plus the config plugin in app.json) - see Runtimes.

No bundler

Without a bundler you compile with the CLI and import the generated loader yourself. Add cpp.js as a dev dependency and a build script:

package.json
{
"scripts": {
"build": "cppjs build -p wasm -a wasm32 -r st -e browser -b release"
},
"devDependencies": {
"cpp.js": "^2.0.0-beta"
}
}

The build writes <name>-wasm-wasm32-st-release.browser.js and its .wasm into the folder named by paths.output. Load the JS file and call the global boot function:

index.html
<script src="./dist/myapp-wasm-wasm32-st-release.browser.js"></script>
<script>
initNative({ path: './dist' }).then(({ MySampleClass }) => {
document.querySelector('#cppMessage').innerHTML = MySampleClass.sample();
});
</script>

You lose dev-mode recompiles and dead-code elimination against your app code; everything else works the same.

Writing your own plugin

The contract is small enough to port to another build tool: resolve header imports to bridge files (resolveId / load), generate a bridge per header and hand back the loader (createBridgeFile, getCppJsScript), compile in the bundle step (createLib, buildWasm), and in dev serve /cpp.js and /cpp.wasm from the build directory while watching paths.native for changes.

my-plugin.js
import {
state, createLib, createBridgeFile, buildWasm, getCppJsScript,
getDependFilePath, getTargetParams, getFilteredBuildTargets,
} from 'cpp.js';
 
const targetParams = getTargetParams({ platform: ['wasm'], arch: ['wasm32'], runtime: ['st'], runtimeEnv: ['browser'] }, true);
const target = getFilteredBuildTargets(targetParams, { buildType: 'release' })?.[0];

Every helper takes that resolved target object, which also carries the output names (jsName, wasmName, dataTxtName). Mirror @cpp.js/plugin-rollup - it is the smallest complete implementation.

Type to search every guide page and section.
↑↓ navigate↵ openesc close