TopoJSON Map
Interactive geographic maps with region fills, points, and routes, powered by D3.
Show visitors by country, mark office locations, or connect cities with routes. TopoJSONMap includes a world dataset, so your first map needs no geometry import or extra dependencies. Maps inherit the same theme, tooltip, legend, and loading options as other Nuxt Charts components.
Color Countries by Value
Pass country records through data.areas and choose a numeric field with value. The component calculates the color domain from your data. The bundled map matches ISO alpha-2, alpha-3, and numeric country codes.
<script setup lang="ts">
const data = {
areas: [
{ id: 'NLD', value: 94, name: 'Netherlands' },
{ id: 'USA', value: 91, name: 'United States' },
{ id: 'DEU', value: 71, name: 'Germany' },
{ id: 'BRA', value: 60, name: 'Brazil' },
{ id: 'FRA', value: 59, name: 'France' }
]
}
</script>
<template>
<TopoJSONMap
:data="data"
:height="400"
value="value"
projection="equalEarth"
:categories="{ value: { name: 'Visitors', color: 'var(--ui-primary)' } }"
hide-legend
aria-label="Visitors by country"
/>
</template>
Use valueDomain to keep the same scale across several maps, colorRange to select the low and high colors, or colorScale to supply your own function. Explicit areaColor overrides automatic coloring; otherwise a row's color takes precedence over its numeric value. Areas without values use --vc-map-feature-color.
Mark Locations
Points use longitude and latitude in degrees. Labels appear in tooltips; showPointLabels also renders them beside the markers. Markers keep their pixel size when the map zooms.
<script setup lang="ts">
const data = {
points: [
{ id: 'ams', latitude: 52.3676, longitude: 4.9041, label: 'Amsterdam' },
{ id: 'nyc', latitude: 40.7128, longitude: -74.006, label: 'New York' },
{ id: 'tyo', latitude: 35.6762, longitude: 139.6503, label: 'Tokyo' }
]
}
</script>
<template>
<TopoJSONMap :data="data" :height="400" :point-size="6" show-point-labels />
</template>
Point styles accept fixed values or functions. For example, :point-size="point => Math.sqrt(Number(point.orders))" sizes markers by their data. Use mapFitToPoints to focus on the visible points. Set heatmapMode to combine nearby points into a blurred layer; individual markers reappear at heatmapModeZoomLevelThreshold (default 4).
Connect Points
Links reference point IDs or point objects through source and target. The default geodesic follows a great-circle route and clips at the date line and globe edge. Use linkCurve="straight" for a straight projected line or linkCurve="arc" for a curved projected connection.
<script setup lang="ts">
const data = {
points: [
{ id: 'ams', latitude: 52.3676, longitude: 4.9041, label: 'Amsterdam' },
{ id: 'nyc', latitude: 40.7128, longitude: -74.006, label: 'New York' },
{ id: 'tyo', latitude: 35.6762, longitude: 139.6503, label: 'Tokyo' }
],
links: [
{ source: 'ams', target: 'nyc', color: 'var(--ui-primary)' },
{ source: 'tyo', target: 'ams', color: 'var(--ui-info)' }
]
}
</script>
<template>
<TopoJSONMap :data="data" :height="400" :link-width="2" show-point-labels show-controls />
</template>
Use Your Own Geography
Pass topoJson or geoJson to replace the bundled map. Supported inputs are a TopoJSON Topology, GeoJSON FeatureCollection, Feature, or geometry. Place your data in public/maps/ and load it with useFetch:
<script setup lang="ts">
const { data: topology, status, error } = await useFetch<MapGeometry>('/maps/regions.topo.json')
const data = { areas: [{ id: 'north', value: 42 }] }
</script>
<template>
<TopoJSONMap
:topo-json="topology ?? undefined"
map-feature-key="regions"
feature-id="region_code"
:data="data"
value="value"
:loading="status === 'pending'"
:error="error?.message"
/>
</template>
mapFeatureKey selects an object inside topology.objects. When omitted, the component chooses countries, land, or the first object. featureId chooses the feature property matched against area.id; it also accepts a function. The default is feature.id.
Area callbacks, events, and tooltips receive the joined area: feature properties, your row fields, properties, and the original feature. GeoJSON polygon winding is normalized for D3 without changing your input.
Choose a Projection
Use projection="naturalEarth" (default), "mercator", "equalEarth", "equirectangular", "orthographic", or "albersUsa". The projection fits the geometry into the available space on resize.
For a configured projection, install d3-geo in your app and pass its projection or a factory:
<script setup lang="ts">
import { geoOrthographic } from 'd3-geo'
const projection = () => geoOrthographic().rotate([-20, -25])
</script>
<template>
<TopoJSONMap :projection="projection" show-sphere show-graticule />
</template>
Set :fit-view="false" when your projection already specifies its scale and translation. Factories create an independent projection per component; a passed projection instance is configured in place during fitting. Globe projections hide features and markers on the back face.
Interaction and Custom Rendering
Maps support drag, touch pinch, and double-click zoom. zoomOnScroll enables wheel zoom. Enable showControls for zoom/reset buttons or call the exposed zoomIn(), zoomOut(), zoomTo(scale), and resetView() methods. fitToPoints(points?) and zoomToFeature(id) focus on data. zoomOnClick enables this behavior when a point or region is clicked.
Keyboard users can focus the map and use arrow keys to pan, +/- to zoom, and 0/Home to reset. Regions, points, and routes expose tooltips on focus and activate with Enter or Space. ariaLabel, ariaDescription, and the hidden data table provide accessible descriptions.
<TopoJSONMap :data="data">
<template #tooltip="{ values, kind }">
<div class="rounded-lg bg-elevated px-3 py-2 text-sm">
{{ kind }}: {{ values.label ?? values.name ?? values.id }}
</div>
</template>
</TopoJSONMap>
The feature, point, and link slots replace individual SVG marks and receive typed values, x, y, path, color, radius, scale, and projection. The default slot renders SVG overlays in the zoomed group and receives width, height, transform, and projection. The legend slot receives items.
Props
Geometry, Layout, and Areas
| Prop | Type | Default | Purpose |
|---|---|---|---|
data | MapData | {} | Optional areas, points, and links. |
topoJson / geoJson | MapGeometry | Bundled world | Custom geometry. geoJson takes precedence. |
mapFeatureKey | string | Auto | TopoJSON object name. |
featureId | string | (feature) => string | number | Feature ID | Join property/accessor. |
height / width | number | string | 400 / '100%' | Container dimensions. |
projection | Name, D3 projection, or factory | 'naturalEarth' | Geographic projection. |
fitView | boolean | true | Fit projection to geometry. |
fitViewPadding | number | 12 | Fit inset in pixels. |
padding | number | Partial<ChartPadding> | undefined | Override fit insets. |
value | Field name or accessor | area.value | Numeric choropleth value. |
valueDomain | [number, number] | Data extent | Color scale bounds. |
colorRange | [string, string] | Area fill → primary | Low/high colors. |
colorScale | (value: number) => string | Auto | Custom color mapping. |
areaColor, areaStroke, areaCursor | string | (area, index) => string | Theme / theme / pointer | Area appearance. |
areaStrokeWidth | number | (area, index) => number | 0.6 | Boundary width in pixels. |
Points and Links
| Prop | Type | Default | Purpose |
|---|---|---|---|
pointColor, pointStrokeColor, pointCursor, pointLabel | String or accessor | Row/theme | Marker appearance and label. |
pointSize / pointRadius | Number or accessor | Row radius / 5 | Pixel radius; pointRadius takes precedence. |
pointStrokeWidth | Number or accessor | 1.5 | Marker outline width. |
showPointLabels | boolean | false | Render point labels. |
linkColor, linkCursor, linkDasharray | String or accessor | Row/theme | Route appearance. |
linkWidth | Number or accessor | Row width / 1.5 | Route width in pixels. |
linkCurve | 'geodesic' | 'straight' | 'arc' | 'geodesic' | Connection shape. |
linkCurvature | number | 0.2 | Arc bend as a fraction of distance. |
mapFitToPoints | boolean | false | Focus on visible markers after updates. |
heatmapMode | boolean | false | Blur the point layer. |
heatmapModeBlurStdDeviation | number | 12 | Heatmap blur. |
heatmapModeZoomLevelThreshold | number | 4 | Switch to markers at this zoom. |
maxDataPoints | number | 2000 | Limit each point/link layer. |
Zoom and Shared Chart Options
| Prop | Type | Default | Purpose |
|---|---|---|---|
disableZoom, zoomOnScroll, showControls, zoomOnClick | boolean | false | Interaction options. |
zoomFactor | number | 1 | Initial/reset scale. |
zoomExtent | [number, number] | [1, 8] | Allowed scale range. |
zoomDuration / duration | number | 250 | Programmatic zoom animation; respects reduced motion. |
showSphere, showGraticule | boolean | false | Globe outline and geographic grid. |
sphereColor, graticuleColor, backgroundColor | string | Theme | Background styling. |
categories, legendPosition, legendStyle, legendVariant, hideLegend | Shared chart options | Bottom center | Legend styling. |
hideTooltip, tooltipVariant, tooltipRoundness | Shared chart options | Chart defaults | Tooltip styling. |
tooltipTitleFormatter | (datum) => string | number | Label/name/ID | Tooltip title. |
yFormatter | (value: number) => string | Number format | Numeric tooltip values. |
theme | ChartTheme | Host tokens | Shared per-map theme. |
loading, loadingLabel, error, emptyLabel | Shared chart options | — | Loading, error, and empty states. |
ariaLabel, ariaDescription, accessibleDataTable | Shared chart options | Table enabled | Accessibility. |
Events
feature-click, point-click, and link-click emit (values, event). mouseenter and mouseleave emit (values, event) for pointer hover and keyboard focus. zoom emits { x, y, k }.
Geometry and routes use D3 geographic projections and geographic paths. Interaction uses D3 zoom.