GeoJSON with Mapbox GL JS
In Mapbox GL JS, GeoJSON is a source type. You add the source, then add one or more layers that reference it.
Add a GeoJSON source and render it
Call map.addSource() with type: 'geojson' and a data value that is either a URL or a GeoJSON object. Then call map.addLayer() for each visual representation you need — the same source can back multiple layers simultaneously.
map.on('load', () => {
map.addSource('zones', {
type: 'geojson',
data: '/delivery-zones.geojson',
})
map.addLayer({
id: 'zones-fill',
type: 'fill',
source: 'zones',
paint: {
'fill-color': '#3388ff',
'fill-opacity': 0.4,
},
})
map.addLayer({
id: 'zones-outline',
type: 'line',
source: 'zones',
paint: { 'line-color': '#3388ff', 'line-width': 2 },
})
})zones-fill renders the polygon interiors; zones-outline renders the borders. Both reference the same 'zones' source — one fetch, two visual layers.
Layer types
Mapbox GL JS has five layer types that work with GeoJSON geometry:
| Type | Renders | Geometry |
|---|---|---|
fill | Filled polygons | Polygon, MultiPolygon |
line | Strokes on lines or polygon outlines | LineString, MultiLineString, Polygon |
circle | Circle markers at each point | Point, MultiPoint |
symbol | Icons or text labels at point locations | Point, MultiPoint |
fill-extrusion | Extruded 3D polygons (requires a height property) | Polygon, MultiPolygon |
A single GeoJSON source can have multiple layers of different types — for example, a fill layer for the interior and a line layer for the stroke.
Update a source at runtime
Call setData() on the source object to replace all features. The map re-renders immediately.
// Replace all features in the source
map.getSource('zones').setData(updatedFeatureCollection)
// Works with a plain object or a URL
map.getSource('zones').setData('https://api.example.com/zones.geojson')When you pass a URL, Mapbox GL JS fetches it and replaces the source data. This is the pattern for polling live data: call setData() with the endpoint on an interval, and the map stays current without a page reload.
Filter features by property
map.setFilter() applies a Mapbox expression to a layer. Features that don't match the expression are hidden — the source data is unchanged.
// Show only active zones
map.setFilter('zones-fill', ['==', ['get', 'status'], 'active'])
// Remove filter
map.setFilter('zones-fill', null)Expressions use the ['get', 'propertyName'] form to read feature properties. ['==', ...], ['>', ...], ['in', ...], and ['all', ...] are the most common operators for GeoJSON property filtering.
Hover highlight
Feature state lets you store transient state (like hover: true) on individual features without modifying the GeoJSON source. The layer paint expression reads the state and applies a different style.
let hoveredId = null
map.on('mousemove', 'zones-fill', (e) => {
if (e.features.length > 0) {
if (hoveredId !== null) {
map.setFeatureState({ source: 'zones', id: hoveredId }, { hover: false })
}
hoveredId = e.features[0].id
map.setFeatureState({ source: 'zones', id: hoveredId }, { hover: true })
}
})
map.on('mouseleave', 'zones-fill', () => {
if (hoveredId !== null) {
map.setFeatureState({ source: 'zones', id: hoveredId }, { hover: false })
}
hoveredId = null
})Reference the feature state in your layer's paint expression:
// In the layer paint
'fill-opacity': ['case', ['boolean', ['feature-state', 'hover'], false], 0.7, 0.4]The ['boolean', ['feature-state', 'hover'], false] form provides a default of false when the state key hasn't been set yet — avoiding a null opacity on first render.
Note: features need numeric id fields in the GeoJSON for setFeatureState to work. If your GeoJSON features don't have id, set promoteId: 'your_property' on the source to promote a property to the feature ID.
Watch out:
map.addSource()andmap.addLayer()must be called insidemap.on('load', ...). Calling them before the map style loads throwsError: Style is not done loadingwith no other indication of what went wrong.
Editing GeoJSON on a Mapbox GL JS map
@element-editor/adapter-maplibre works with MapLibre GL JS and Mapbox GL JS (with Mapbox GL JS, TypeScript needs one cast on the map instance). It adds drawing and editing as a layer on top of your existing map setup.
For React-specific patterns, see GeoJSON with React. For format reference, see What is GeoJSON?.
Continue reading
- GeoJSON with React
How to load and render GeoJSON in React — with react-leaflet, react-map-gl, state management for selected features, and the useMemo trap.
- GeoJSON with Leaflet.js
How to load, style, and interact with GeoJSON in Leaflet — including L.geoJSON(), per-feature styling, popups, and react-leaflet.
- FoundationsWhat is GeoJSON?
GeoJSON is an open standard (RFC 7946) for encoding geographic features as JSON. Learn the format, geometry types, and how it's used.

Mike Acler
Design engineer and creator of Element Editor, an open-source map drawing and GeoJSON editing library for React. Writes about web cartography and GIS tooling.