DotPlot
DotPlot compares a few named values as dots on one shared scale, with or without stems to zero.
- encodes
- dot position on a common scale
- precision
- high
- nodes
- ≤ 2 per row + text (rows ≤ 7)
DotPlot places each named value as a dot on one shared scale, which is the minimum ink per comparison. Reach for it over
bars when the scale doesn't start at zero: position stays readable where a truncated bar length would overstate the
difference. Without stem the domain fits the data and position is the read; with stem the domain is forced through
zero and the read becomes magnitude.
Install
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 },];Try it
When to use it
Use it for KPI leaderboards, named comparisons in cards, and rows where a truncated bar would overstate the difference. Avoid it above 7 rows; for a time series use Sparkline.
Sizing
Variants
Edge cases
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 and 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, so the real values around it keep the spacing they'd have in a fully
populated set. It is also excluded from the category count in the summary.
Two dots that would land on the same spot on adjacent rows de-overlap by half a unit. Labels truncate by character count and drop entirely once rows get dense. Both are arithmetic on the layout; nothing is measured from the rendered text.
Four homes
Accessibility
The accessible name carries the count and the extremes: "3 categories. Highest Ada 96, lowest Kim 41." The interactive entry roves rows and announces each one 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
| Prop | Type | Notes |
|---|---|---|
| data* | { label; value }[] | Named values. |
| stem | boolean | Hairline from zero — flips to a magnitude read (zero-anchored domain forced). |
| highlight | number | string | Accent one category. |
| label | "value" | "none" | Value text beside each dot (drops out under 8-unit rows). |
| animate | boolean | interactiveOpt-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.