v3 beta on npm · React Native support · iOS & Android

GDAL in the browser.
And now on mobile.

GDAL 3.13 with its driver registry, compiled to WebAssembly for the web and to native modules for iOS and Android. Read, write, and transform raster and vector formats entirely on-device. No server. No upload. No native install.

Read the docs
What's new in v3 v2 → v3 guide v3 announcement
API
Full GDAL, zero glue
React Native
iOS · Android
Threading
Workers + multi-threaded
GeoTIFF / COG
+ JPEG · ZSTD · LERC
Dependencies
add · drop · tune
Optimization
dead-code elimination
License
LGPL → MIT
Engine
cpp.js
i.Why gdal3.js

Full GDAL, everywhere JavaScript runs.

One library, the same API across three runtimes. Process raster and vector geodata where the user already is, without standing up a backend or shipping native binaries to every device.

01
The full GDAL API.
Not a hand-picked subset: every GDAL and OGR class and method is exposed, the same surface you would use in C++, now in JavaScript. Built on GDAL 3.13.1, with 189 format drivers compiled in.
FROM v3.0 · GDAL 3.13.1
02
Raster + vector, one API.
One module exposes gdal_translate, ogr2ogr, gdalwarp, gdal_rasterize, and (new in v3) gdalbuildvrt, gdaldem, gdal_grid, GCP georeferencing and the class-level Dataset/Driver API.
PARITY WITH NATIVE CLI FLAGS
NEW IN v3
03
Runs on React Native.
Real native GDAL over JSI on iOS and Android (New Architecture, Expo supported), not wasm in a webview, so browser memory limits don't apply. Same JavaScript surface as the web.
PLATFORM iOS & ANDROID 7+ · ARM64 + X86_64
04
OPFS & big files.
Work on multi-gigabyte rasters through the Origin Private File System: files persist across reloads and never need to fit in a single upload, because there is no upload.
STORAGE OPFS-BACKED VIRTUAL FS
05
Bundlers, solved.
Official cpp.js plugins for Vite, Webpack, Rollup, Rspack and Metro wire the wasm, data and worker assets for you. The asset-path and copy-plugin gymnastics that filled the v2 issue tracker are gone.
PLUGINS VITE · WEBPACK · ROLLUP · RSPACK · METRO
06
Private by design.
Files never leave the device. No telemetry. No third-party API calls. Released under the MIT License; the default build is LGPL-2.1 (free in closed-source apps), and you can drop the copyleft deps for a fully permissive build.
MIT License
07
Powered by cpp.js.
v3 is built on cpp.js, the C++ to JavaScript toolchain. The API is full GDAL with no hand-written glue.
Compiles GDAL 3.13.1 with PROJ, GEOS, SQLite, SpatiaLite, libtiff, libgeotiff, Expat, zlib and more. Versions and licenses →
Reproducible Docker build WebAssembly + React Native Typed from C++ headers
Website: cpp.js.org · GitHub: bugra9/cpp.js
ii.Quick start

Pick a runtime. Convert a file.

A minimal reprojection pipeline. The same input, the same output, on every supported runtime. Just a different way to read the file in.

main.ts · vite + @cpp.js/plugin-vite
// vite.config.ts, one line:  plugins: [cppjs()]
import 'gdal3.js/Dataset.h';
import { initCppJs, Gdal } from 'gdal3.js/Gdal.h';

const Module = await initCppJs({ useWorker: true, fs: { opfs: true } });

// put the user's file into OPFS; gdal sees it as /opfs/…
const dir = await navigator.storage.getDirectory();
const fh  = await dir.getFileHandle(file.name, { create: true });
const ws  = await fh.createWritable(); await ws.write(file); await ws.close();

const ds   = await Gdal.open(`/opfs/${file.name}`);
const opts = await Module.toVector('VectorString',
  ['-f', 'GPKG', '-t_srs', 'EPSG:3857']);
const out  = await ds.vectorTranslate('/opfs/out.gpkg', opts);
await out.close();  // out.gpkg persists in OPFS, survives reloads
v3 wasm: ≈14 MB gzipped on first load (189 drivers + PROJ data) · lazy-loaded · cached after first run Full getting-started guide →
iii.Supported formats

If GDAL reads it, so do you.

The driver registry of the GDAL 3.13 build is bundled with every release: no à-la-carte builds. The list below is a sample; search the registry for what you need.

.tifGeoTIFF GTiffRW
.geojsonGeoJSON GeoJSONRW
.shpESRI Shapefile ESRI ShapefileRW
.gpkgGeoPackage GPKGRW
.fgbFlatGeobuf FlatGeobufRW
.kmlKeyhole Markup KMLRW
.gpxGPS Exchange GPXRW
.csvComma-Separated CSVRW
.cogCloud-Optimized GeoTIFF COGRW
.pngPortable Network Graphics PNGRW
.jp2JPEG 2000 JP2OpenJPEGRW
.mbtilesMBTiles MBTilesRW
.ncNetCDF netCDFRO
.hdf5Hierarchical Data Format HDF5RO
.lasASPRS LiDAR LASRO
.gmlGeography Markup GMLRW
Showing a 16-driver sample · 189 drivers ship in the build Browse the drivers →
iv.Compatibility

One package, five runtimes.

gdal3.js targets every JavaScript environment that has a file system or a buffer. The same exports work across all of them; only the entry-point import path differs.

Environment Read Write OPFS / FS Workers Bundle
Browser
CHROME 90+ · FIREFOX 89+ · SAFARI 15.2+
OPFS ESM / CJS / UMD
Node.js
v18+ · v20 LTS RECOMMENDED
node:fs ESM / CJS
React Native NEW
NEW ARCHITECTURE · EXPO & BARE
device FS native threads Native module (JSI)
Edge runtimes
EXPERIMENTAL · DEDICATED EDGE BUILD
in-memory check platform wasm size limits
v.Where it goes

What people build with it.

A few patterns we see most often. The library doesn't care what you put on top of it. Converter, viewer, pipeline, app. It just exposes GDAL.

α
PATTERN · IN-APP UTILITY
Drop a converter into your web app.
Let users bring their own geodata. Read whatever they upload, hand them back the format your pipeline needs, all in their tab, no upload size limits.
SEEN IN · geosmith.devOPFS-BACKED
β
PATTERN · SERVERLESS PIPELINES
Raster pipelines without a GDAL install.
Run reprojection, warping, format conversion on Node Lambdas, Vercel functions, or Bun workers, without shipping a native GDAL binary in your image.
RUNTIME · node 18+NO NATIVE DEPS
γ
PATTERN · OFFLINE MOBILE GIS
Read shapefiles on the trail.
Open and reproject geodata inside a React Native app. Works on an offline phone, the same code you use on the web. Field tools, survey apps, expedition viewers.
NEW IN · v3PLATFORMS · iOS · Android
δ
PATTERN · UPLOAD PREVIEW
Inspect uploads before they hit the server.
Pull gdalinfo client-side: CRS, bounds, band stats, feature counts. Reject malformed files at upload time, not after they're stored.
SAVES · server bandwidthCLIENT-SIDE gdalinfo
vi.Shaped by its issue tracker

You asked. It shipped.

Most of what's new started as somebody's GitHub issue, with merged contributions from GDAL's own lead maintainer among them. A sample of requests that became features.

01
"Missing codec JPEG / ZSTD."
JPEG-, ZSTD- and LERC-compressed GeoTIFFs & COGs used to fail on open. Community PRs wired libjpeg-turbo, zstd and LERC into the build, for reading and writing.
VIA #103 · #99 → PRs #105 / #107 / #108
02
"Where did my output go?"
The virtual filesystem confused everyone. It is now a first-class API (readDir, unlink, rename, copyFile, mkdir), plus clearFS() to reset between conversions.
VIA #56 · #44 · #90 → PR #109 + v3 FS API
03
"What about the other tools?"
v3's zero-glue binding makes whole tool families cheap to expose: gdalbuildvrt, gdaldem, gdal_grid, nearblack, footprint, multidim and GCP georeferencing are in; gdal_polygonize (#93) is next.
VIA #58 · #95 → v3 API · 13 PROGRAMS
04
Open options, config, logs.
-oo open options, GDAL config options, log/error handlers and /vsizip/-style open prefixes all arrived as community PRs against v2, and carry into v3.
VIA #41→#42 · #40 · #63 · #71→#62
05
"iOS Safari kills the tab."
Mobile-browser memory limits capped real workloads. v3's answer is structural: run GDAL as a native module on iOS and Android instead of wasm in a browser tab.
VIA #96 → REACT NATIVE SUPPORT
06
Fixed before you hit it.
A worker-promise leak that crashed long tile pipelines, planetary PDS formats for NASA workflows, Electron absolute paths: community-reported, merged, released.
VIA #104 · #70 + #66 · #89
vii.Looking for the converter?

The converter has a new home.

The GIS converter app, built on this library, moved to its own domain so it can grow into a full toolkit. Same tool, same files, same privacy guarantees. Just a different URL.

CONVERTER · MOVED

Convert geodata. In your browser.

168 formats in, the format you need out. Reproject, warp, repackage. Files never leave the device. Now lives at its own address so the converter team can ship faster without disturbing the library.

Open geosmith.dev It's the same app. Free, no signup, no upload.
WAS gdal3.js.org geosmith.dev
viii.Read the docs

Three doors into the library.

Start with the path that matches what you're trying to do: get something running, look up a function, or understand how the wasm side works.

ix.License

MIT at the core, permissive when you need it.

Released under the MIT License. The prebuilt build is LGPL only because of the libraries it bundles. You can drop those for an MIT/BSD-only build.

The code
MIT License
Everything authored in this repo: the JS API, the C++/embind bridge, the build tooling.
The default build
LGPL-2.1
The prebuilt wasm and native packages bundle GEOS, SpatiaLite and libiconv. Still fine in closed-source apps.
Need zero copyleft
Permissive
Override the cpp.js config to leave those three out and ship an MIT/BSD-only build.
Full license breakdown
x.FAQ

Questions, quickly answered.

The things people ask first. If yours isn't here, open an issue on GitHub. We read them all.

1 Where did the converter app go? +

The converter that used to live at gdal3.js.org moved to geosmith.dev. Nothing else changed. Same files, same wasm pipeline, same privacy guarantees (your data never leaves the device).

We split it out because the converter is becoming a full geospatial toolkit, with its own roadmap. Keeping it separate lets the library stay small and focused on being a great wasm GDAL build, and lets the app team ship on their own schedule.

If you bookmarked the old URL, follow the big orange button at the top of this page, or go directly to geosmith.app.

2 How do I migrate from v2 to v3? +

v3 is a ground-up rewrite on cpp.js, and the API changed with it. The headlines:

• You import GDAL's headers as modules (import { initCppJs, Gdal } from 'gdal3.js/Gdal.h'), and a bundler plugin (Vite, Webpack, Rollup, Rspack or Metro) wires the wasm, data and worker assets automatically. No more copy-plugin and paths configuration.
• The v2 helpers (Gdal.ogr2ogr(dataset, args)) became dataset methods: ds.vectorTranslate(outPath, opts), plus class-level access to Dataset, Driver and GCP.
• v2 (2.8.x) stays on npm and keeps working; migrate when you're ready.

The full diff lives in the v2 → v3 migration guide.

3 Where does my output go? +

Into a virtual filesystem, the single most-asked question in the issue tracker. In the browser there is no real disk, so GDAL writes into an in-memory or OPFS-backed FS. You pick the output path (e.g. /opfs/out.gpkg), then read it back as bytes, trigger a download, or leave it in OPFS where it survives page reloads.

The FS is a first-class API now: readDir, unlink, rename, copyFile, mkdir, and clearFS() to reset between conversions (community PR #109). In Node and React Native there's no hop at all: GDAL reads and writes your real filesystem directly.

4 Can I open remote files: /vsicurl/, S3, WMS? +

Not yet over the network. /vsicurl/ needs a curl that speaks browser-fetch, and that work is tracked in issue #67. What works today: fetch the file yourself and hand the bytes to gdal3.js, plus local VSI layers like /vsizip/ (zipped Shapefiles open directly) and in-memory /vsimem/.

5 My GeoTIFF fails with "missing codec JPEG / ZSTD"? +

Fixed. Older builds shipped libtiff without the JPEG, ZSTD and LERC codecs, so compressed GeoTIFFs and COGs failed to open. Community PRs (#105, #107, #108) wired libjpeg-turbo, zstd and LERC into the build; v3 includes all three, for reading and writing (-co COMPRESS=JPEG|ZSTD|LERC|LERC_ZSTD).

6 What's the bundle cost? +

The v3 full build is roughly 14 MB gzipped on first load: ~11.5 MB of wasm plus ~2.2 MB of driver/PROJ data. That's the price of the complete driver registry with the new codecs; it loads lazily when you call initCppJs() and is cached after the first run.

If that's too much, slimmer driver subsets are the plan for the minimal bundle, and on React Native the question disappears. GDAL ships inside the app binary, not over the network.

7 How does it compare to native GDAL? +

Functionally: it's the same GDAL, with the same option flags. If your script works with ogr2ogr on the command line, the same flags work here.

Performance-wise, in the browser expect roughly 1.5× to 3× the wall-clock time of native, depending on the operation. On iOS and Android there is no wasm penalty at all; v3 runs GDAL as a real native library. In Node, if you need maximum throughput and can install native binaries, a native binding will still be faster; gdal3.js trades that for zero native dependencies.

8 Does it work offline? +

Yes. After the wasm and data files are cached (first run), the library runs entirely offline, including in a service-worker-cached PWA or a React Native app that has no network. There are no runtime calls to a GDAL server.

9 What does the license mean for my app? +

gdal3.js is released under the MIT License, but the default build bundles a few LGPL libraries (GEOS, SpatiaLite, libiconv), so the package you ship is LGPL-2.1-or-later by default. That's still fine for a commercial, closed-source app. Your application code stays closed; LGPL only asks that users can replace the LGPL parts and that changes to those libraries are shared. Need a fully permissive build? Override the cpp.js config to exclude GEOS, SpatiaLite and libiconv; everything else (GDAL, PROJ, …) is MIT/BSD, so there's no copyleft obligation (with reduced geometry/encoding support). See the license page for the full breakdown.

This was settled early in the project's history (issue #36, GPL → LGPL by community request).