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 tool | Install | Registered in |
|---|---|---|
| Vite | @cpp.js/plugin-vite | vite.config.js |
| Rollup | @cpp.js/plugin-rollup | rollup.config.js |
| Webpack | @cpp.js/plugin-webpack + @cpp.js/plugin-webpack-loader | webpack.config.js |
| Rspack | @cpp.js/plugin-webpack + @cpp.js/plugin-webpack-loader | rspack.config.mjs |
| Metro (React Native) | @cpp.js/plugin-react-native + @cpp.js/plugin-metro | metro.config.js |
| None | cpp.js | a 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
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.
Webpack
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.
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.
@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:
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:
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.
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.