Stat
KPI with a count-up value, a tube-drawn icon that lights when live, a delta judged by direction, a sparkline or goal.
Built on Glow Chart, Icons and Skeleton.
vs. the previous 30 days
vs. the previous 30 days
vs. the previous 30 days
vs. the previous 30 days
Installation
npx shadcn@latest add https://neonkit.heyaadi.com/r/stat.jsonUsage
import { Gauge } from "@/components/ui/icons"
import { Stat, StatGroup } from "@/components/ui/stat"
<StatGroup>
<Stat icon={Gauge} label="p95 latency" value={182} unit="ms" delta={-12.4} positive="down" trend={latency} />
<Stat label="Active workspaces" value={1284} delta={3.1} />
</StatGroup>Examples
Live
The icon's tube lights up, the sparkline's end pings and each update flashes. The queue fills toward its alert line.
Toward a goal
goal draws a tube that fills to the target, and its share is read out under it.
When down is good
positive="down" turns a falling error rate green and a rising bill red. Neutral metrics stay grey.
Loading
Skeletons hold the value, delta and sparkline in place, so nothing jumps when data lands.
API reference
Stat
Renders a div with role="group", named by its label. Accepts every native div attribute.
| Prop | Type | Default |
|---|---|---|
label | ReactNodeWhat the number is. Sentence case, no colon. Wraps rather than truncating. | None |
icon | IconA NeonKit icon, drawn as a tinted glass tube with a core. It lights up when live. | None |
value | numberA number counts up and is formatted; a string is shown as is. | None |
format | (value: number) => stringFormats the value, including every frame of the count-up. | None |
formatOptions | Intl.NumberFormatOptionsOptions for the default formatter, e.g. currency. Decimals follow the value otherwise. | None |
locale | stringLocale for the default formatter. Fixed by default so server and client agree. | "en-US" |
unit | ReactNodeSmaller text after the value, such as ms or %. | None |
delta | numberChange since the comparison period. Its sign sets the arrow. | None |
formatDelta | (delta: number) => ReactNodeFormats the delta's absolute value. The arrow carries the sign. | 12.4% |
positive | "up"Which direction is good. Sets green or red; neutral stays grey. | "up" |
trend | number[]Draws a Sparkline. | None |
trendPlacement | "bottom"Full width under the value, or small beside it. Beside it wraps under the value when the cell is narrow. | "bottom" |
goal | numberA target for a numeric value. Draws a tube meter that fills toward it. | None |
helper | ReactNodeA line under the value, usually the comparison period. | None |
countUp | booleanCounts up on mount and eases between later values. Skipped under reduced motion. | true |
live | booleanLights the icon (or shows a heartbeat dot), pings the sparkline end and flashes on every update. | false |
loading | booleanSkeletons in place of the value, delta and sparkline. Sets aria-busy. | false |
size | "sm"Value set at 24, 32 or 44px. | "md" |
color | NeonColorPalette name or hex for the sparkline and live dot. Falls back to --neon-accent, then cyan. | inherited |
StatGroup
A grid of stats split by hairlines. Accepts every native div attribute.
| Prop | Type | Default |
|---|---|---|
columns | 1Columns at full width. It steps down to two, then one on narrow screens. | count, up to 4 |
variant | "plain"Hairlines only, or with an outer border and surface. | "plain" |
color | NeonColorColor for every stat inside. | inherited |
Accessibility
- Assistive tech reads the final value once; the counting digits are hidden from it.
- The delta says Up or Down in words, so color is never the only signal.
- Live stats don't announce each tick, which would talk over the page. Put a live region around the one value that matters if you need it.
- The count-up reserves the final value's width, so the delta and sparkline don't shift while it runs.
- In a narrow cell the delta and an end sparkline wrap under the value instead of overlapping it.
- Under prefers-reduced-motion the value appears at once and the heartbeat dot holds still.