Data display
StatPro
A KPI strip: one surface with dividers between metrics, each with a label, a large value, a colored delta, an optional sparkline and a comparison line.
npx prodular-ui add statPart of Prodular UI Pro. Preview and read the docs freely, install with a license. See pricing
Revenue
Sparklines under each value. Churn passes invert, so a drop reads as good news.
Monthly recurring revenue
$184,320
Up8.4%vs $170,040 last 30 days
Net revenue retention
116%
Up3.1 ptsvs 112.9% last quarter
Logo churn
2.1%
Down0.8 pts26 accounts lost
Paying customers
1,284
Up4.6%57 new this month
"use client"
import { InfoIcon } from "lucide-react"
import { Stat, StatComparison, StatContent, StatDelta, StatLabel, Stats, StatSparkline, StatValue,} from "@/components/ui/stat"import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
const mrr = [148, 151, 150, 156, 159, 158, 163, 167, 166, 171, 175, 178, 181, 184]const nrr = [109, 110, 110, 111, 112, 111, 112, 113, 113, 114, 114, 115, 115, 116]const churn = [2.9, 2.8, 2.9, 2.7, 2.6, 2.6, 2.5, 2.4, 2.4, 2.3, 2.2, 2.3, 2.1, 2.1]const newCustomers = [9, 12, 8, 14, 11, 13, 10, 16, 12, 15, 13, 17, 14, 19]
export default function StatRevenue() { return ( <Stats> <Stat> <StatLabel>Monthly recurring revenue</StatLabel> <StatContent> <StatValue>$184,320</StatValue> <StatDelta value={8.4} /> </StatContent> <StatComparison>vs $170,040 last 30 days</StatComparison> <StatSparkline data={mrr} label="MRR rising over 14 weeks" /> </Stat> <Stat> <StatLabel> Net revenue retention <Tooltip> <TooltipTrigger render={<button type="button" aria-label="About net revenue retention" className="rounded-sm outline-none focus-visible:ring-3 focus-visible:ring-ring/40" />} > <InfoIcon /> </TooltipTrigger> <TooltipContent>Expansion minus contraction and churn, trailing 12 months</TooltipContent> </Tooltip> </StatLabel> <StatContent> <StatValue>116%</StatValue> <StatDelta value={3.1} format={(v) => `${v.toFixed(1)} pts`} /> </StatContent> <StatComparison>vs 112.9% last quarter</StatComparison> <StatSparkline data={nrr} className="text-chart-2" /> </Stat> <Stat> <StatLabel>Logo churn</StatLabel> <StatContent> <StatValue>2.1%</StatValue> <StatDelta value={-0.8} invert format={(v) => `${v.toFixed(1)} pts`} /> </StatContent> <StatComparison>26 accounts lost</StatComparison> <StatSparkline data={churn} className="text-chart-3" /> </Stat> <Stat> <StatLabel>Paying customers</StatLabel> <StatContent> <StatValue>1,284</StatValue> <StatDelta value={4.6} /> </StatContent> <StatComparison>57 new this month</StatComparison> <StatSparkline data={newCustomers} variant="bar" label="New customers per week" className="text-chart-4" /> </Stat> </Stats> )}Field operations
Five metrics. On narrow containers the strip wraps to two columns and the odd one spans the last row.
Jobs completed
412
Up6.2%vs 388 last week
First-time fix rate
87.4%
Up1.9%Target 85%
Avg. response time
42 min
Down11.3%vs 47 min last week
Technicians on shift
36
No change0.0%4 on call tonight
Open work orders
58
Up9.4%12 past SLA
import { Stat, StatComparison, StatContent, StatDelta, StatLabel, Stats, StatValue,} from "@/components/ui/stat"
const metrics = [ { label: "Jobs completed", value: "412", delta: 6.2, note: "vs 388 last week" }, { label: "First-time fix rate", value: "87.4%", delta: 1.9, note: "Target 85%" }, { label: "Avg. response time", value: "42 min", delta: -11.3, invert: true, note: "vs 47 min last week" }, { label: "Technicians on shift", value: "36", delta: 0, note: "4 on call tonight" }, { label: "Open work orders", value: "58", delta: 9.4, invert: true, note: "12 past SLA" },]
export default function StatOperations() { return ( <div className="w-full max-w-5xl"> <Stats> {metrics.map((m) => ( <Stat key={m.label}> <StatLabel>{m.label}</StatLabel> <StatContent> <StatValue>{m.value}</StatValue> <StatDelta value={m.delta} invert={m.invert} /> </StatContent> <StatComparison>{m.note}</StatComparison> </Stat> ))} </Stats> </div> )}Anatomy
<Stats> <Stat> <StatLabel>Monthly recurring revenue</StatLabel> <StatContent> <StatValue>$184,320</StatValue> <StatDelta value={8.4} /> </StatContent> <StatComparison>vs last 30 days</StatComparison> <StatSparkline data={series} /> </Stat></Stats>Guidance
Use it when
- The top of a dashboard or report, for three to five numbers people check every day.
- Pair every delta with a comparison line so the number has a reference: vs last 30 days, vs target.
- Sparklines when the shape of the trend matters more than the exact history.
Avoid
- A grid of separate cards, one per metric. Use one Stats strip.
- More than five metrics in one strip. Split them into two strips with headings.
- Coloring the value itself. Only the delta carries color.
Rules for AI agents
Published in llms-full.txt and the registry, so coding agents follow them when they build with this component.
- Render one Stats container with one Stat per metric. Never wrap a Stat in a Card.
- Pass the raw signed change to StatDelta value. Set invert for metrics where down is good: churn, latency, cost, open tickets, errors.
- Use format on StatDelta for units that are not percentages, for example points or minutes.
- Put StatValue and StatDelta inside StatContent so they share a baseline.
- Give StatSparkline a label when the trend carries meaning, and color it with text-chart-1 to text-chart-5. Do not use raw colors.
- Use variant="plain" when the strip sits inside an existing Card.
API
Stats
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "card" | "plain" | "card" | card draws the surface. plain is for use inside an existing card. |
StatDelta
| Prop | Type | Default | Description |
|---|---|---|---|
| value | number | The signed change. The sign sets the arrow. | |
| invert | boolean | false | Down is good. A negative value turns green. |
| format | (abs: number) => string | Formats the absolute change. Defaults to one decimal with a percent sign. |
StatSparkline
| Prop | Type | Default | Description |
|---|---|---|---|
| data | number[] | The series, oldest first. | |
| variant | "area" | "line" | "bar" | "area" | Shape of the sparkline. |
| label | string | Screen reader description of the trend. |