TreeRings
TreeRings draws one ring per period, oldest at the center, with each ring's thickness set by its value.
- encodes
- radial ring thickness ∝ per-period value
- precision
- medium
- nodes
- ≤ 4 (merged ring path + highlighted ring + centre dot + label)
TreeRings shows how growth accumulated period over period, the way a tree records it: one ring per period, oldest at the
center, each ring's thickness carrying 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 rather than the wedges. The current period is
drawn 1.5× heavier in the accent color, weight and color together. For exact per-period reads use 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
Use it for account or company age, a cohort-age marker in a table cell, or a per-period growth story in a KPI card. Avoid it for exact per-period reads (SparkBar), for more than twenty-four periods, and for non-cumulative series.
Sizing
Variants
Stroke rings are the default: at twenty-four pixels, hairlines keep the disc quiet and let the accent ring stay loud.
rings="fill" draws filled annuli instead. Pass total for the cohort-age story, and the disc fills only Σdata/total
of the radius, so a young account reads as part-grown.
Edge cases
TreeRings enforces no minimum visual thickness. A near-zero period looks near-zero, and a zero-value period collapses its two boundaries onto each other.
Four homes
Accessibility
The accessible name summarizes the growth: "8 periods; latest 14, biggest 22 in period 5." The interactive entry steps the rings from the center 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"). |
| size | number | Rings box edge in viewBox units (default 24). |
| fontSize | number | Type size of the gutter label, in viewBox units. Defaults from `size`. |
| 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.