DOCS · GETTING STARTED · BUNDLERS

Bundlers

One integration model: a cpp.js plugin per bundler. The framework on top (React, Vue, Svelte, anything) doesn't matter, the plugin works at the bundler layer. Packages, cppjs.config.js and the GDAL code are the same everywhere, the Quick start covers those; this page is just the plugin and config each bundler needs.

Pick your bundler

BUNDLERPLUGINCONFIG
Vite@cpp.js/plugin-vitevite.config.ts
Webpack / Rspack@cpp.js/plugin-webpackwebpack.config.mjs
Rollup@cpp.js/plugin-rolluprollup.config.mjs
Metro (React Native)@cpp.js/plugin-metro + RN pluginsmetro.config.js
No bundler / Nodecppjs build CLI, or CDNcppjs.config.mjs

Vite (React, Vue, Svelte, vanilla)

shell
npm install -D @cpp.js/plugin-vite
vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";              // or react() / svelte() / nothing
import viteCppjsPlugin from "@cpp.js/plugin-vite";

export default defineConfig({
  plugins: [vue(), viteCppjsPlugin()],
});

Dev-server asset wiring, worker bundling and the COOP/COEP headers for the multi-threaded build are handled for you. The framework choice only changes the first plugin in the array, it's how the bundled converter app (Vue 3) is set up.

Webpack & Rspack

shell
npm install -D @cpp.js/plugin-webpack
webpack.config.mjs / rspack.config.mjs
import CppjsWebpackPlugin from "@cpp.js/plugin-webpack";

const cppjs = new CppjsWebpackPlugin();

export default {
  module: {
    rules: [cppjs.getRule()],            // loader for .h / .i imports
  },
  plugins: [cppjs],
  devServer: cppjs.getDevServerConfig(), // COOP/COEP headers for the multi-threaded build
};

The plugin gives you three things: a loader rule (getRule()), the plugin instance, and a dev-server config with the cross-origin-isolation headers. Rspack uses the same @cpp.js/plugin-webpack through its webpack-compatible API.

Rollup

shell
npm install -D @cpp.js/plugin-rollup
rollup.config.mjs
import rollupCppjsPlugin from "@cpp.js/plugin-rollup";

export default {
  plugins: [rollupCppjsPlugin()],
};

Metro (React Native & Expo)

React Native runs native GDAL, so it uses the platform packages (not wasm) and its own plugins, and the cppjs.config.js imports the native configs instead:

shell
npm install gdal3.js @gdal3.js/android @gdal3.js/ios
npm install -D @cpp.js/plugin-metro @cpp.js/plugin-react-native @cpp.js/plugin-react-native-ios-helper
cppjs.config.js (React Native)
import ios from "@gdal3.js/ios/cppjs.config.mjs";
import android from "@gdal3.js/android/cppjs.config.mjs";

export default {
  dependencies: [ios, android],
  paths: { config: import.meta.url },
};
metro.config.js
const { getDefaultConfig } = require("expo/metro-config"); // or @react-native/metro-config
const { mergeConfig } = require("metro-config");
const CppjsMetroPlugin = require("@cpp.js/plugin-metro");

const config = getDefaultConfig(__dirname);
module.exports = mergeConfig(config, { ...CppjsMetroPlugin(config) });

iOS native code builds at pod install; Android builds in Gradle. New Architecture is supported. File access uses real device paths, with no virtual FS.

Vanilla / no bundler / CDN

No bundler plugin? You still have two routes: compile your own code with the cpp.js CLI, or grab a prebuilt build.

Build it yourself with cppjs build

If your project has its own native C++ (the src/native pattern from the quick start), the cpp.js CLI compiles it to WebAssembly without any bundler. Declare gdal3.js and an output folder in the config, and add a build script:

package.json + cppjs.config.mjs
// package.json
"scripts": {
  "build": "cppjs build -p wasm -a wasm32 -r st -e browser -b release"
}

// cppjs.config.mjs
import wasm from "@gdal3.js/wasm/cppjs.config.mjs";
export default {
  dependencies: [wasm],
  paths: { config: import.meta.url, output: "dist" },
};

With your C++ in src/native, npm run build emits dist/<project>-wasm-wasm32-st-release.browser.js (plus its .wasm and .data). Load that one script and call the global initCppJs, pointing path at the output folder:

index.html
<script src="./dist/myapp-wasm-wasm32-st-release.browser.js"></script>
<script>
  initCppJs({ path: "./dist" }).then(({ Native }) => {
    document.body.textContent = Native.version();
  });
</script>

Building for Node is the same command with -e node instead of -e browser (or import the prebuilt Node build from @gdal3.js/wasm-bundle); on Node, GDAL reads and writes the host filesystem directly, with no virtual FS.

Prebuilt UMD & CDN

No build step at all: the @gdal3.js/wasm-bundle browser build is gdal3.js already run through cppjs build and exposes the global initCppJs(). Self-host its .js, .wasm and .data.txt, or load them from a CDN (jsDelivr serves the npm package). Use the single-threaded (st) build cross-origin; the multi-threaded build needs same-origin hosting with COOP/COEP.

index.html
<script src="https://cdn.jsdelivr.net/npm/@gdal3.js/wasm-bundle@beta/dist/gdal3js-wasm-wasm32-st-release.browser.js"></script>
<script>
  initCppJs({ path: "https://cdn.jsdelivr.net/npm/@gdal3.js/wasm-bundle@beta/dist/" })
    .then(({ Gdal }) => {
      // Gdal.openEx(...), Gdal.getDriverCount(), ...
    });
</script>