DOCS · UPGRADING

Migrating from v2 to v3

v3 is a ground-up rewrite on cpp.js: instead of a hand-maintained wrapper around five GDAL programs, the GDAL C++ classes are bound automatically and your bundler wires the assets. This page maps every v2 call to its v3 equivalent.

STATUS v3 is in beta (npm install gdal3.js@beta). The v2 line (2.8.x) stays on npm and keeps receiving fixes; the JPEG/ZSTD/LERC codec PRs landed on the v2 branch in April 2026. Migrate when it suits you.

What changed, in one paragraph

In v2 you imported one package, called initGdalJs({ path, … }) with hand-configured asset paths, and used program-shaped helpers like Gdal.ogr2ogr(dataset, args). In v3 you import GDAL's C++ headers as JavaScript modules (import { initCppJs, Gdal } from 'gdal3.js/Gdal.h'), and a cpp.js bundler plugin (Vite, Webpack, Rollup, Rspack or Metro) wires the wasm, data and worker assets automatically. Programs became methods on the Dataset class, and the same imports run as a real native library on iOS and Android.

Installation

v2 → v3 · install & wire the bundler
# v2
npm install gdal3.js@2

# v3
npm install gdal3.js@beta
npm install -D @cpp.js/plugin-vite   # or -webpack / -rollup / -rspack / -metro
vite.config.ts · the whole bundler setup
import { defineConfig } from 'vite';
import cppjs from '@cpp.js/plugin-vite';

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

That replaces all of v2's CopyWebpackPlugin blocks, ?url imports and paths configuration, the source of most v2 setup issues. Framework-specific notes live in the frameworks guide.

API mapping (old → new)

v2v3
import initGdalJs from 'gdal3.js'import { initCppJs, Gdal } from 'gdal3.js/Gdal.h' (+ side-effect imports for Dataset.h, Driver.h, GCP.h, SubdatasetInfo.h)
import … from 'gdal3.js/node.js'Same import everywhere: the plugin picks the right build per platform
initGdalJs({ path, paths })initCppJs(): asset paths resolved by the bundler plugin
initGdalJs({ useWorker })initCppJs({ useWorker: true })
Gdal.open(file) (a File object)Gdal.open(path): put bytes in OPFS or /vsimem/ first; see VFS guide
Gdal.open(f, opts, ['vsizip'])Gdal.openEx(path, flags, allowedDrivers, openOptions, siblingFiles), or open a /vsizip/… path directly
Gdal.ogr2ogr(ds, args)ds.vectorTranslate(outPath, opts)
Gdal.gdal_translate(ds, args)ds.translate(outPath, opts)
Gdal.gdalwarp(ds, args)Gdal.warp(destPath, dstDS, [srcDS], opts)
Gdal.gdal_rasterize(ds, args)ds.rasterize(outPath, opts)
Gdal.gdalinfo(ds) / ds.infods.info(opts) · ds.vectorInfo(opts) · ds.multiDimInfo(opts)
args: ['-f', 'GeoJSON']const opts = await Module.toVector('VectorString', ['-f', 'GeoJSON'])
output name via outputNameYou pass the full output path as the first argument
Gdal.getFileBytes(path)Outputs are real files in OPFS / host FS. Read them with the platform FS (browser: navigator.storage); list with Module.getFileList(dir)
getOutputFiles() / .all listds.getFileList() (per dataset) or Module.getFileList(dir)
Gdal.close(ds)ds.close()
Gdal.drivers.raster / .vectorawait Module.toArray(await Gdal.getDrivers()), each with isRaster() / isVector() / isWritable()
config: { env: { KEY: 'V' } }Gdal.setConfigOption('KEY', 'V')
logHandler / errorHandlerGdal.getLastErrorMsg(), Gdal.errorReset(), see error handling

Breaking changes

Common pitfalls

Forgetting Module.toVector for options

Program methods take a C++ vector<string>, not a JS array. Convert once: const opts = await Module.toVector('VectorString', ['-f', 'GPKG']).

Writing into a directory that doesn't exist

GDAL won't create intermediate directories: that's the classic v2 error ERROR 4: Failed to create … No such file or directory (issue #69). Create the tree first: await Module.FS.mkdirTree('/opfs/myapp/output').

Expecting output on the real disk in the browser

There is no real disk in a tab. Outputs land in the virtual filesystem (OPFS-backed if you init with fs: { opfs: true }). The VFS guide covers reading results back and offering downloads.

Multi-file formats (Shapefile)

A Shapefile conversion produces .shp + .shx + .dbf (+ .prj). Collect them with ds.getFileList() instead of assuming one path, same story as v2's .all attribute (issue #45).

Unknown-option errors from v2 are gone, mostly

v2's hand-rolled argument parsing rejected some real GDAL switches (-a_ullr #53, -gcp #95). v3 hands your option vector straight to GDAL's own options parsers (GDALTranslateOptions & friends), so the native CLI documentation applies as-is.

Will v2 still be maintained?

Yes: 2.8.x remains on npm and still receives fixes (most recently the GeoTIFF codec work and a worker memory-leak fix, April to May 2026). New feature work happens on v3. There is no removal date for v2.