TreeRings
React tree rings chart — per-period growth as concentric rings, oldest at the center. Tiny accessible SVG microchart; RSC-safe static or /interactive.
TreeRings answers "how did growth accumulate, period over period?" like the rings of a tree: the oldest period is at the
centre and each new period adds a ring outward, whose thickness is that period's value. The channel is thickness,
not area — equal thickness at a larger radius spans more area (the ring illusion), so read the thicknesses, not the
wedges; exact per-period reads steer to SparkBar.
Install
import { TreeRings } from "@microcharts/react/tree-rings";// years — real values under “Sample data” below<TreeRings data={years} unit="years" periodWord="year" title="Account age" />Sample data
const years = [8, 12, 10, 18, 22, 15, 20, 14];Try it
When to use it
- Good for — account or company age at a glance, a cohort-age marker in a table cell, or a per-period growth story in a KPI card.
- Avoid for — exact per-period reads (SparkBar), many periods (over twenty-four), or non-cumulative series.
Sizing
Variants
Edge cases
Four homes
Why this default
Stroke rings are the default because at twenty-four pixels hairlines keep the disc quiet and let the accent ring — the
current period, drawn 1.5× heavier in the accent color (weight and color, never color alone) — stay loud. The channel is
radial thickness, and the docs say so explicitly, because equal thickness at a larger radius spans more area.
TreeRings never enforces a minimum visual thickness: a near-zero period looks near-zero, and a zero-value period
collapses its two boundaries onto each other. Pass total for the cohort-age story — the disc then fills only
Σdata/total of the radius, so a young account reads as part-grown.
Accessibility
The accessible name summarizes the growth — "8 periods; latest 14, biggest 22 in period 5." The interactive entry steps the rings from the centre out with ←/→ (or hover), announcing each period as "Year 5: 22."
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* | number[] | Per-period growth, oldest first. |
| highlight | "last" | "none" | number | Which period's ring to pick out. |
| total | number | Expected lifetime Σ — the disc fills only Σdata/total. |
| rings | "stroke" | "fill" | Boundary rings (default) or filled annuli. |
| periodWord | string | Singular period noun for the summary (default "period"). |
| unit | string | Plural period noun for the summary (default "periods"). |
| 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
MusicStaff
React music staff chart — ordered values as notes on a miniature staff. Tiny accessible SVG microchart; RSC-safe static or /interactive.
CitySkyline
React city skyline chart — group size as building height and activation as lit windows. Tiny accessible SVG microchart; RSC-safe static or /interactive.