KPI trend
Free · MITKPI tile family: the number rolls in on view and on every change (NumberFlow), the delta badge pops and pulses, the sparkline draws itself (SparklineDraw), and a group lines tiles up with hairline dividers.
Installation
$ npx shadcn@latest add https://goodui.dev/r/kpi-trend.jsonThe file lands in components/goodui/kpi-trend.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/kpi-trend works too.
Usage
import { KpiTrend, KpiTrendGroup } from "@/components/goodui/kpi-trend"
export function Overview({ revenue, bookings, noShow }: { revenue: number[]; bookings: number[]; noShow: number[] }) {
const last = (a: number[]) => a[a.length - 1] ?? 0
return (
<KpiTrendGroup columns={3}>
<KpiTrend label="Revenue" value={last(revenue)} format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }} delta={12.4} deltaLabel="vs last week" data={revenue} />
<KpiTrend label="Bookings" value={last(bookings)} delta={3.1} deltaLabel="vs last week" data={bookings} color="var(--chart-2)" />
<KpiTrend label="No-show rate" value={last(noShow)} suffix="%" delta={-0.8} invertDelta deltaLabel="vs last week" data={noShow} color="var(--chart-5)" />
</KpiTrendGroup>
)
}When to use
- The row of headline metrics at the top of a dashboard: revenue, bookings, no-show rate, average ticket.
- Live numbers: values and sparklines that update every few seconds without re-rendering the layout.
- Any metric with a change vs the previous period and a 7–14 point trend.
- A metric that needs full variant, tone, shadow and shape layers: use the Core stat-card.
- A single hero number without trend: counter-up or number-flow alone.
- A chart with axes: area-chart.
| If you need | Use |
|---|---|
| Four metrics in one framed row | <KpiTrendGroup columns={4}> with KpiTrend tiles |
| Wide tile with the trend beside the number | layout="row" size="lg" |
| Compact list of metrics | size="sm" layout="row" |
| Rate where down is good | invertDelta (no-shows, churn, wait time) |
| Money | format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }} |
Examples
Wide, interactive tile
<KpiTrend label="Monthly recurring revenue" value={48210} format={usd} delta={12.4} deltaLabel="vs September" data={mrr} layout="row" size="lg" interactive onClick={open} />Translated delta text
<KpiTrend label="Umsatz" value={48210} locale="de-DE" format={{ style: "currency", currency: "EUR" }} delta={12.4} labels={{ increase: "mehr", decrease: "weniger", noChange: "unverändert" }} />API reference
KpiTrend
| Prop | Type | Default |
|---|---|---|
| label* Metric name above the value. | React.ReactNode | — |
| value* | number | — |
| color Sparkline color. Defaults to the theme's first chart color. | string | "var(--chart-1)" |
| format Intl.NumberFormat options, e.g. { style: "currency", currency: "USD", maximumFractionDigits: 0 }. | Intl.NumberFormatOptions | — |
| locale | string | "en-US" |
| prefix Text before the number. | string | "" |
| suffix Text after the number, e.g. "%". | string | "" |
| delta Change vs the previous period, e.g. 12.4 for +12.4%. | number | — |
| deltaSuffix Unit after the delta number. | string | "%" |
| deltaDecimals Decimals of the delta. | number | 1 |
| invertDelta A drop is good news (no-shows, churn, wait time). | boolean | — |
| deltaLabel Muted text after the delta, e.g. "vs last week". | React.ReactNode | — |
| data Sparkline values, oldest first. | number[] | — |
| pointLabels One label per sparkline point, shown in its tooltip. | string[] | — |
| layout stack: sparkline under the number. row: sparkline on the end side. | "stack" | "row" | "stack" |
| size | "sm" | "md" | "lg" | "md" |
| interactive Hover lift for tiles that link somewhere. | boolean | — |
| labels Text for built-in controls and screen readers; pass translations here. | Partial<KpiTrendLabels> | — |
Also accepts every <div> attribute.
KpiTrendGroup
| Prop | Type | Default |
|---|---|---|
| columns Tiles per row on wide screens; they stack to one and two columns below. | 2 | 3 | 4 | 4 |
Also accepts every <div> attribute.
Accessibility
| Tab | Focuses the sparkline (from sparkline-draw). |
| ArrowLeft / ArrowRight, Home / End | Moves the sparkline's highlighted point; the value shows in its tooltip. |
- The value is rendered once as screen-reader text with the final formatted number; the rolling digits are aria-hidden.
- The delta badge carries sr-only text "+12.4% increase" (or decrease / no change) through labels; color and arrow are extra.
- The sparkline is sparkline-draw with its own summary label (the tile label) and keyboard scrubbing.
- KpiTrendGroup draws hairline dividers with background color, so tiles have no nested borders.
Motion
- On first view (30% visible) the number rolls up from 0 with NumberFlow digit springs (stiffness 140, damping 20), the delta badge pops in (0.45 s, overshooting ease) and the sparkline draws itself (sparkline-draw, 0.9 s).
- A new value rolls the digits; a new delta pulses a 8px ring in its tone for 0.6 s; new sparkline data redraws the line.
- interactive tiles lift 2px on hover (0.2 s).
- Reduced motion: digits snap, no pop, no pulse, the sparkline appears drawn.
- Each tile owns one IntersectionObserver until it is seen; updates re-render the tile only.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- value is the current number; delta is the percent change (or any number with deltaSuffix). Pass invertDelta for metrics where a drop is good.
- data is the sparkline (oldest first) and pointLabels names its points; keep both the same length.
- format is Intl.NumberFormat options; prefix/suffix wrap the formatted number ("%").
- Use KpiTrendGroup for a framed row; standalone tiles get their own rounded border.
FAQ
How is this different from stat-card?
stat-card is the layered Core primitive (variants, tones, shadows, shapes). KpiTrend is the chart-group tile tuned for live updates: rolling digits, pulsing delta and a redrawing sparkline, with a group that lines tiles up.
Will the numbers roll from 0 on every render?
No: only on first view. Later changes roll from the previous value.
Related
KPI card with seven variants, tones, shadows and sizes: count-up or rolling NumberFlow values, auto-toned delta badges (invertible for no-show rates), line, area or bar sparklines that draw in, icons, footers and a skeleton state.
Smooth monotone sparkline that draws itself in, fades in a gradient area, shows point values on hover or arrow keys, and redraws when the data changes.
Digits roll to the new value with locale formatting, prefixes and suffixes.
"+12.4%" change badge whose digits roll, arrow rotates to the new direction and background pulses green or red, with an inverted mode for metrics where down is good.
Counts from 0 (or from) to the value with an ease-out curve when it scrolls into view, with locale formatting, once or replay-on-return, and counts from the current value when it changes.