QuadrantDot
React quadrant dot chart — a focal 2-D point against peers on a split cross. Tiny accessible SVG microchart; RSC-safe static or /interactive.
QuadrantDot answers "where does this item sit in the 2×2 — against the field?". A focal dot is placed by 2-D position, a hairline cross marks the split (the domain midpoints by default, always overridable but never hidden), and the peers show as tiny muted ghosts. The read is quadrant membership first; exact position is the second read — which is why it lives at glyph scale.
Install
import { QuadrantDot } from "@microcharts/react/quadrant-dot";// item, backlog — real values under “Sample data” below<QuadrantDot data={item} field={backlog} xLabel="effort" yLabel="impact" title="Effort vs impact" />Sample data
// a prioritization backlog — effort (x) vs impact (y)const item = { x: 3, y: 9 };const backlog = [ { x: 2, y: 8 }, { x: 8, y: 9 }, { x: 3, y: 7 }, { x: 9, y: 2 }, { x: 7, y: 3 }, { x: 1, y: 1 }, { x: 5, y: 6 }, { x: 6, y: 8 }, { x: 4, y: 3 }, { x: 8, y: 5 }, { x: 2, y: 4 }, { x: 7, y: 7 }, { x: 3, y: 2 }, { x: 6, y: 1 },];Try it
When to use it
- Good for — the classic prioritization 2×2 (one cell per initiative in a table), an effort-vs-impact read in a KPI card, any "which quadrant, against the field" decision.
- Avoid for — a full scatter plot (MicroScatter) or a single ranked list (MiniBar).
Sizing
Variants
Edge cases
When every field value on one axis equals the focal's, that axis's domain collapses to a point — the split line on that axis is dropped rather than drawn at an arbitrary spot, while the other axis's split still renders. A peer exactly on top of the focal is still counted (distance 0) and sorts first in the interactive entry's nearest-first order. A non-finite focal position draws no marks at all — no cross, no tint, no peers — and carries the "No data." summary, the same degenerate path an empty series takes elsewhere in the library.
Four homes
Why this default
24×24, because the 2×2 is a position read, not a scatter plot — you want to know the quadrant at a glance, and the
exact coordinates only when you look closer. The split is data, not decoration: it renders wherever the split truly is
and is always overridable, never invisible. With the axes unlabeled at glyph size, the title and summary are
load-bearing — skipping them is the one named anti-pattern for this chart, so always pass xLabel, yLabel, and a
title.
Accessibility
The accessible name states the focal position and the axis-relative quadrant — "Impact 9, effort 3 — in the
high-impact, low-effort quadrant." — adding how crowded that quadrant is when a field is given, or your own words
via quadrants={["quick win", …]}. The interactive entry starts on the focal, then cycles peers nearest-first — each
announced with its coordinates and quadrant.
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* | { x; y } | The focal item's 2-D position. |
| field | { x; y }[] | The peer set — omit for a lone glyph. |
| xDomain | [number, number] | The x-axis range (default: derived from the data); domain stays the y-axis. |
| domain | [number, number] | The y-axis range (default: derived from the data). |
| split | [number, number] | The quadrant boundary (default domain midpoints) — never hidden. |
| quadrants | [TL, TR, BL, BR] | Names in reading order — summaries only, never rendered. |
| xLabel / yLabel | string | Axis nouns for the summary — pass them, the axes are unlabeled at glyph size. |
| region | boolean | Faint tint on the focal's quadrant (default true; false for dense grids). |
| 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.
Related charts
DataDiff
React data diff chart — before/after series divergence for a release or experiment. Tiny accessible SVG microchart; RSC-safe static or /interactive.
CyclePlot
React cycle plot chart — seasonal slots with within-slot drift on a shared spine. Tiny accessible SVG microchart; RSC-safe static or /interactive.