GeoJSON with React
React has no built-in GeoJSON rendering. The two most common libraries for adding a map to a React app are react-leaflet and react-map-gl. Both accept GeoJSON directly.
Load GeoJSON with fetch
The simplest approach is a custom hook that fetches a GeoJSON file and stores it in state. The url is the dependency — the effect re-runs whenever it changes.
import { useState, useEffect } from 'react'
import type { FeatureCollection } from 'geojson'
function useGeoJSON(url: string) {
const [data, setData] = useState<FeatureCollection | null>(null)
useEffect(() => {
fetch(url)
.then(r => r.json())
.then(setData)
}, [url])
return data
}Call it at the component level: const data = useGeoJSON('/features.geojson'). It returns null on the first render (before the fetch resolves), so guard against null before passing data to a map layer.
Render with react-leaflet
react-leaflet exposes a <GeoJSON> component that wraps Leaflet's L.geoJSON(). Pass your FeatureCollection as the data prop.
import { MapContainer, TileLayer, GeoJSON } from 'react-leaflet'
import { useMemo } from 'react'
export function MyMap({ data }: { data: FeatureCollection }) {
const stableData = useMemo(() => data, [data])
return (
<MapContainer center={[51.5074, -0.1276]} zoom={12} style={{ height: 400 }}>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" />
<GeoJSON key={JSON.stringify(stableData)} data={stableData} />
</MapContainer>
)
}Note: react-leaflet's <GeoJSON> doesn't update when data changes — pass a new key to force remount when features change.
Render with react-map-gl
react-map-gl uses a source/layer model that mirrors Mapbox GL JS. A <Source> holds the data; one or more <Layer> components describe how to render it.
import Map, { Source, Layer } from 'react-map-gl'
import type { FillLayer } from 'react-map-gl'
const layerStyle: FillLayer = {
id: 'zones-fill',
type: 'fill',
paint: { 'fill-color': '#3388ff', 'fill-opacity': 0.4 },
}
export function MyMap({ data }: { data: FeatureCollection }) {
return (
<Map
initialViewState={{ longitude: -0.1276, latitude: 51.5074, zoom: 12 }}
style={{ height: 400 }}
mapStyle="mapbox://styles/mapbox/light-v11"
>
<Source type="geojson" data={data}>
<Layer {...layerStyle} />
</Source>
</Map>
)
}Defining layerStyle outside the component avoids a new object reference on every render. For dynamic styles that depend on state, wrap in useMemo.
Track selected feature
Click handlers in react-leaflet attach via onEachFeature. Store the selected Feature in state and derive the style from it.
import { useState } from 'react'
import type { Feature } from 'geojson'
export function MyMap({ data }: { data: FeatureCollection }) {
const [selected, setSelected] = useState<Feature | null>(null)
return (
<MapContainer center={[51.5074, -0.1276]} zoom={12} style={{ height: 400 }}>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png" />
<GeoJSON
data={data}
onEachFeature={(feature, layer) => {
layer.on('click', () => setSelected(feature))
}}
style={(feature) => ({
fillColor: feature?.id === selected?.id ? '#ff6b35' : '#3388ff',
fillOpacity: 0.5,
})}
/>
{selected && <p>Selected: {selected.properties?.name}</p>}
</MapContainer>
)
}The style callback runs on every render, so the selected feature gets the highlight colour immediately on state change.
Watch out: Passing a new object reference for
dataon every render causesreact-leaflet's<GeoJSON>to re-render the entire layer every tick. Wrap static data inuseMemoor define it outside the component. Forreact-map-gl,<Source>handles reference changes more gracefully butuseMemois still the right pattern.
Editing GeoJSON features in React
Rendering GeoJSON with react-leaflet or react-map-gl is one part of the story. Drawing new features, moving vertices, cutting holes, and boolean operations require an editing layer on top of the map.
@element-editor/react is an open-source MIT library with a drop-in <ElementEditor /> component that brings its own MapLibre map (or a Leaflet map, from @element-editor/react/leaflet) plus the full editing UI. It takes your FeatureCollection as initialData and returns the updated FeatureCollection via onChange. To add editing to a map you already render with react-leaflet or MapLibre, use @element-editor/adapter-leaflet or @element-editor/adapter-maplibre.
For Leaflet-specific patterns, see GeoJSON with Leaflet.js. For Mapbox GL JS patterns, see GeoJSON with Mapbox GL JS.
Continue reading
- GeoJSON with Leaflet.js
How to load, style, and interact with GeoJSON in Leaflet — including L.geoJSON(), per-feature styling, popups, and react-leaflet.
- GeoJSON with Mapbox GL JS
How to add a GeoJSON source to Mapbox GL JS, render it as fill, line, or circle layers, update it at runtime, and filter by property.
- 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.