DocumentationComponentsTheme Creator
Theme CreatorGitHubIntroduction
InstallationUsageThemingMotion
Components
ChartArea ChartBar ChartLine ChartLive Line ChartComposed ChartPie ChartFunnel ChartRadar ChartRadial ChartSankey ChartScatter ChartCandlestick ChartSunburst ChartChoropleth ChartActivity Heatmap
AI agentsContributing
  1. Components
  2. Choropleth Chart

Choropleth Chart

Regional data on a map — each region filled by its value's bin on a sequential ramp, with a stepped legend, a keyboard walk over named regions, and optional zoom, graticule and textures.

$ pnpm add @tessinaui/ui

When to use

  • One measure across regions, where location matters. Users by country, revenue by state, turnout by district: the map shows where the high and low regions are, and whether they cluster.
  • To spot a regional pattern at a glance. A north–south split, a coast against an interior, a single outlier region.
  • Beside the numbers. Pair the map with a ranked table (Accessible name and data table); the map shows the pattern and the table gives the values.

When not to use

  • To compare exact values between regions. A tint is a bin, not a number. Use a sorted BarChart.
  • When the regions' sizes would mislead. A large, sparsely populated region draws the eye whatever its value. Show a rate (per person, per store) rather than a total, or use a bar chart.
  • For change over time. Use LineChart; one map per period is hard to compare.
  • For places that are points. Stores, offices and events are locations, not areas. This chart fills regions only.
  • For a handful of regions. With five regions, a list or a bar chart reads faster than a map.

Installation

Terminal
pnpm add @tessinaui/ui d3-geo

The package ships no map data. Load GeoJSON for your regions; to convert a TopoJSON atlas, add topojson-client:

Terminal
pnpm add topojson-client

Usage

example.tsx
import * as React from "react";
import { feature } from "topojson-client";
import { ChoroplethChart } from "@tessinaui/ui/choropleth-chart";
import type { ChoroplethFeatureCollection } from "@tessinaui/ui/choropleth-chart";

const users = { France: 9100, Germany: 12800, Spain: 4300 };

export function UsersByCountry() {
  const [countries, setCountries] = React.useState<ChoroplethFeatureCollection>();

  React.useEffect(() => {
    // Any TopoJSON or GeoJSON of your regions. Convert TopoJSON once.
    fetch("/countries-110m.json")
      .then((res) => res.json())
      .then((topology) =>
        setCountries(
          feature(topology, topology.objects.countries) as ChoroplethFeatureCollection,
        ),
      );
  }, []);

  return (
    <ChoroplethChart
      data={countries}
      loading={!countries}
      featureId="name"
      values={users}
      label="Most users are in Germany and France"
    />
  );
}

values is keyed by the id featureId reads: the feature's own id by default, or a properties key such as "name". A function of the feature works too.

Examples

Default

Countries take one of five bins by quantile: each bin holds about the same number of countries, which suits long-tailed data. Countries with no value are hatched, never drawn as zero.

US states

projection="albersUsa" draws the 50 states and DC with Alaska and Hawaii moved beside the rest. Use it with US data only: regions it cannot draw are left out of the map and of the list.

Thresholds

scale={{ thresholds }} sets the cuts yourself, in the units the reader thinks in. formatValue spells the values in the legend, the names and the tooltip.

Zoom

zoomEnabled adds zoom in, zoom out and reset buttons under the map. Once zoomed in, drag to move around. A region reached with the keyboard is brought into view.

Textures and graticule

patterns gives each bin a texture beside its tint (solid, dots, two hatches, crosshatch), so the map still reads in greyscale and in print; the legend repeats them. graticule draws lines of latitude and longitude and the globe's outline.

Accessible name and data table

On a coloured plate

Inside a coloured Surface the bins keep their tints; the seams, the no-data hatch, the legend text, the zoom buttons and the focus ring follow the plate.

States

API Reference

PropTypeDefaultDescription
dataChoroplethFeatureCollection—The regions, as GeoJSON. Unset or empty shows emptyState.
valuesRecord<string, number> | (feature) => number | null | undefined—A value per region. A region with no finite value is "no data".
featureIdstring | (feature) => string | number | null | undefinedthe feature's idHow a region's id is read: a properties key, or a function. The id keys values.
featureNamestring | (feature) => stringproperties.nameHow a region's name is read.
getFeatureLabel(feature, value: number | null) => string—A region's full accessible name. Default: "France, 9,100".
formatValue(value: number) => stringIntl.NumberFormat(locale)Spells values in names, the tooltip and the legend.
scale"quantile" | "quantize" | { thresholds: number[] } | (value) => number"quantile"How values become bins. A function returns the bin, 0 being the lightest.
stepsnumber5Number of bins, 2–5. Ignored by { thresholds }.
projection"equalEarth" | "naturalEarth1" | "mercator" | "albersUsa" | projection"equalEarth"A preset, or any projection with fitWidth and fitExtent. The chart fits it to the data.
tooltipbooleantrueRegion name and value on hover and keyboard focus; a tap holds it open on touch.
legendbooleantrueThe bins and their ranges. Hidden, it stays for assistive tech.
zoomEnabledbooleanfalseZoom buttons and drag-to-pan while zoomed in.
graticulebooleanfalseLines of latitude and longitude, and the globe's outline.
patternsbooleanfalseA texture per bin beside its tint.
localestring"en-US"Number format and name order.
labelsPartial<ChoroplethChartLabels>EnglishEvery string the chart writes: the list name, "no data", the zoom buttons and announcement.
labelstring—Accessible name; renders the chart as a role="figure".
descriptionstring—Visually hidden summary for assistive tech.
loadingbooleanfalseRender the skeleton.
errorReactNode—Render an error message instead of the map.
emptyStateReactNode"No data to display"Shown when there is no region to draw.
dir"ltr" | "rtl"inheritedReading direction. The map itself does not mirror.

Bins and colour

  • Quantile (the default) puts about the same number of regions in each bin. It keeps a few very large values from pushing every other region into the lightest bin.
  • Quantize splits the range between the smallest and largest value into equal widths. Use it when the values are spread evenly.
  • Thresholds are cuts you choose. A region is in the bin of the last cut it reaches.
  • The ramp runs from light to dark in five steps of --chart-1, so it follows the theme. Each step is opaque, so a bin reads the same on any ground.
  • Regions without a value take the page's muted fill and a sparse hatch, so they differ from the lightest bin without colour.

Accessibility

  • Give every map a label. State the finding ("Most users are in the United States, India and Brazil"), not "Map of users". Put the numbers in description.
  • Offer the values as a table. A tint is a range, not a number (Accessible name and data table).
  • Keyboard. The regions are one list in alphabetical order, with one Tab stop. The arrow keys step through it, Home and End jump to the first and last region, Page Up and Page Down move ten, and typing a letter jumps to the next region starting with it. The focused region is outlined over its neighbours and its tooltip opens.
  • Names. Each region is named with its value ("France, 9,100", or "Greenland, no data") and described by its legend bin ("4,300–9,100").
  • Zoom. The zoom buttons are the way in for touch and keyboard; dragging is never the only way. Each zoom step is announced, and a button at its limit stays focusable.
  • Colour. Turn on patterns where the map must read without colour.
  • Touch. Small regions are smaller than a fingertip. A tap focuses the region and holds its tooltip open; zoom in to reach small regions, or use the table.
  • Screen readers. Not yet verified with VoiceOver, NVDA or JAWS, which is why the component is in beta.
Sunburst ChartActivity Heatmap

On this page

When to useWhen not to useInstallationUsageExamplesDefaultUS statesThresholdsZoomTextures and graticuleAccessible name and data tableOn a coloured plateStatesAPI ReferenceBins and colourAccessibility