GeoJSON with D3.js
D3 renders GeoJSON by combining a path generator with a map projection. The path generator converts geographic coordinates to SVG path strings; the projection handles the coordinate transformation.
Load and render
d3.json() fetches and parses the file. d3.geoPath() takes the projection and returns a function that converts each GeoJSON feature to an SVG d attribute string. fitSize() scales and centers the projection to fill the SVG element exactly.
import * as d3 from 'd3'
const width = 960
const height = 500
const svg = d3.select('#map').append('svg').attr('width', width).attr('height', height)
const geojson = await d3.json('/world-countries.geojson')
const projection = d3.geoNaturalEarth1().fitSize([width, height], geojson)
const path = d3.geoPath(projection)
svg.selectAll('path')
.data(geojson.features)
.join('path')
.attr('d', path)
.attr('fill', '#ccc')
.attr('stroke', '#fff')Each feature in geojson.features becomes one SVG <path> element. The .join('path') call handles enter, update, and exit — adding new elements when features are added and removing them when they are removed.
Projections
The projection you choose determines how geographic coordinates map to screen pixels. D3 ships with over 15 projections; these five cover the most common cases.
| Projection | Best for |
|---|---|
geoNaturalEarth1 | World maps — visually balanced, area distortion is moderate |
geoMercator | Web mapping standard — shapes are familiar, area near poles is inflated |
geoAlbersUsa | USA maps — Alaska inset is built in, scales Hawaii automatically |
geoOrthographic | Globe view — one hemisphere at a time, configurable rotation |
geoConicEqualArea | Choropleth maps — area is preserved, which matters when shading by density |
Switch projections by replacing geoNaturalEarth1 with any other name; the rest of the pipeline stays the same.
Choropleth: colour by property
d3.scaleSequential maps a numeric domain to a continuous colour interpolator. d3.extent() returns [min, max] from the feature array, which becomes the scale domain. Features with a null or undefined value fall through to the '#eee' default.
const colorScale = d3.scaleSequential()
.domain(d3.extent(geojson.features, d => d.properties.value))
.interpolator(d3.interpolateBlues)
svg.selectAll('path')
.data(geojson.features)
.join('path')
.attr('d', path)
.attr('fill', d => d.properties.value != null
? colorScale(d.properties.value)
: '#eee'
)d3.interpolateBlues is one of D3's built-in sequential colour schemes. Alternatives: d3.interpolateViridis, d3.interpolateRdYlGn, d3.interpolateOranges. All accept a value in [0, 1] and return a CSS colour string.
To add a legend, build a d3.scaleSequential with the same domain and interpolator, render it as a gradient <rect>, and add axis ticks using d3.axisBottom.
Tooltips
Attach mouseover and mouseleave handlers to show feature data on hover:
const tooltip = d3.select('body').append('div')
.style('position', 'absolute')
.style('pointer-events', 'none')
.style('display', 'none')
svg.selectAll('path')
.data(geojson.features)
.join('path')
.attr('d', path)
.attr('fill', '#ccc')
.on('mouseover', (event, d) => {
tooltip
.style('display', 'block')
.style('left', `${event.pageX + 10}px`)
.style('top', `${event.pageY - 20}px`)
.text(d.properties.name ?? d.properties.NAME ?? '')
})
.on('mouseleave', () => tooltip.style('display', 'none'))event.pageX and event.pageY are the raw mouse coordinates relative to the document. Adding 10px horizontal offset keeps the tooltip from sitting directly under the cursor.
Watch out: D3 projections transform
[longitude, latitude]→ SVG[x, y]internally. Do not pre-transform coordinates before passing them to D3 —geoPathapplies the projection itself. Pre-transforming produces a double-projection and places features in the wrong location.
For world country GeoJSON data sources, see World countries GeoJSON. For GeoJSON format reference, see What is GeoJSON?.
D3 renders GeoJSON; it doesn't let users edit it. For drawing and editing GeoJSON interactively, open the Element Editor playground.
Continue reading
- World countries GeoJSON: where to get it and how to use it
The best sources for world country GeoJSON data, how resolution levels affect file size, and how to filter and simplify for performance.
- FoundationsGeoJSON examples
Ready-to-use GeoJSON examples for Point, LineString, Polygon, MultiPolygon, and FeatureCollection — plus where to download free GeoJSON datasets.
- 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.