Rendering Large Geospatial Datasets in React Without Freezing the Map

Suman SharmaSuman Sharma

On EUDR work I've had to show tens of thousands of farm plots on one map, with validation status, filters, and selection. The first naive version froze the browser. Here's what fixed it.

The problem

  • Big GeoJSON takes time to download, parse (main thread), and upload to the GPU.
  • React re-renders can re-add map layers or re-pass huge objects.
  • Complex polygons with thousands of vertices each slow down everything.

1. Keep heavy data out of React state

const mapRef = useRef<maplibregl.Map | null>(null); const [selectedId, setSelectedId] = useState<string | null>(null); // Update styles, not data, when selection changes useEffect(() => { mapRef.current?.setFeatureState( { source: "plots", id: selectedId ?? "" }, { selected: true }, ); }, [selectedId]);

Use feature state and filter expressions for highlights and filters instead of rebuilding the data.

2. Use vector tiles for big static layers

Pre-tile large datasets so the browser only loads what's visible at the current zoom:

  • Generate tiles with tippecanoe
  • Serve as PMTiles from static storage, with no tile server needed
  • Use MapLibre's PMTiles protocol to read them

3. Simplify geometry per zoom

At zoom 6 you don't need 2,000 vertices per plot. Simplify for low zooms (tippecanoe does this automatically; for dynamic data, simplify server-side with PostGIS ST_SimplifyPreserveTopology).

4. Use deck.gl for very large or dynamic data

deck.gl renders on the GPU and handles millions of points well. It works with MapLibre as an overlay or interleaved layer.

import { MapboxOverlay } from "@deck.gl/mapbox"; import { ScatterplotLayer } from "@deck.gl/layers"; const overlay = new MapboxOverlay({ layers: [ new ScatterplotLayer({ id: "sensors", data: points, // typed arrays are faster still getPosition: (d) => [d.lon, d.lat], getRadius: 30, getFillColor: [234, 88, 12], }), ], }); map.addControl(overlay);

5. Move parsing off the main thread

Parse and transform large datasets in a Web Worker, then pass the result (ideally as transferable typed arrays).

6. Fetch only what's visible

For dynamic data, query by bounding box and zoom from the API, backed by a spatial index (PostGIS guide).

Frequently asked questions

How many GeoJSON features can a web map handle?

It depends on geometry complexity and device, but large GeoJSON files (many megabytes) quickly slow down parsing and rendering. Beyond that, switch to vector tiles or GPU-based rendering like deck.gl.

Should map state live in React state?

Not the heavy data. Keep the map instance and large datasets outside React state, and only put small UI state (selected feature, filters) in React.

What is PMTiles?

A single-file archive format for map tiles that can be served from static storage via HTTP range requests, without a tile server.

Contact

Get in Touch

Want to call me? book a call and I'll respond in the available time slot. I will ignore all soliciting.