Skip to content
microcharts
ReferenceCoreDotPlot

DotPlot

React dot plot chart — values as dots on a shared scale for precise comparison. Tiny accessible SVG microchart; RSC-safe static or /interactive.

DotPlot answers "how do a few named values compare on one scale?" with the minimum ink per comparison. Dots over bars when the scale doesn't start at zero — position lies less than truncated bar length.

DotPlot
interactive · 5.04 kB · static · 2.51 kB

Install

Import & usereact/dot-plot
import { DotPlot } from "@microcharts/react/dot-plot";// team — real values under “Sample data” below<DotPlot data={team} title="Review scores" />
Sample data
const team = [  { label: "Ada", value: 96 },  { label: "Kim", value: 41 },  { label: "Sam", value: 88 },  { label: "Noor", value: 73 },  { label: "Lee", value: 60 },];
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — KPI leaderboards, named comparisons in cards, rows where a truncated bar would lie.
  • Avoid for — more than 7 rows or time series (Sparkline).

Sizing

Variants

stems
values + highlight

Edge cases

no data
single category
null keeps its row

An empty series has nothing to compare, so no dots draw and the accessible name reads "No data." A single category still renders normally — one dot, one label — with the summary's "category"/"categories" wording matching the count ("1 category. Highest Only 42, lowest Only 42."). A null value draws no dot and no label for its row, but the row still occupies its slot in the vertical rhythm — the real values around it keep the same spacing they'd have in a fully populated set — and it's excluded from the category count in the summary, not counted as a comparison with nothing.

Four homes

Why this default

Without stem the domain fits the data (a position read); with stem the domain is forced through zero (a magnitude read) — the prop flips the honesty regime, and the docs say so. Coincident dots on adjacent rows de-overlap by half a unit; labels truncate by character count and drop entirely when rows get too dense — all pure arithmetic, nothing measured.

Accessibility

The accessible name carries count and extremes — "3 categories. Highest Ada 96, lowest Kim 41." The interactive entry roves rows and announces each with its rank ("Ada: 96 — 1st of 3.").

The interactive entry follows the shared interaction contract: arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape. On touch, a tap pins and a drag scrubs.

Props

PropTypeNotes
data*{ label; value }[]Named values.
stembooleanHairline from zero — flips to a magnitude read (zero-anchored domain forced).
highlightnumber | stringAccent one category.
label"value" | "none"Value text beside each dot (drops out under 8-unit rows).
animatebooleaninteractiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts