Usage
import { StatCard } from "@voila.dev/ui/stat-card";<StatCard.Root>
<StatCard.Header>
<StatCard.Label>Projects published</StatCard.Label>
<StatCard.Delta trend="up">+12%</StatCard.Delta>
</StatCard.Header>
<StatCard.Value>1,284</StatCard.Value>
</StatCard.Root>The label and the delta share the header row; the value sits under them on its own line, so a row of cards lines its numbers up whether or not each one has a delta to show.
trend picks the arrow and the colour; it is separate from the text because a
falling number is not always bad — cancellations going down is a down trend
you would want to read as good.
Recipes
A sparkline for the shape of the number
StatCard.Chart is a full-bleed slot pinned to the bottom edge: it cancels the
card's bottom padding and the card clips the chart to its rounded corners.
Constrain the height on the Chart.Root itself and drop the margins, since a
sparkline has no axes to make room for.
<StatCard.Chart>
<Chart.Root
config={config}
data={data}
x={{ key: "month", type: "point" }}
y={{ keys: ["projects"] }}
className="aspect-auto h-16 w-full"
margin={{ top: 4, right: 0, bottom: 0, left: 0 }}
interactive
>
<Chart.Cursor />
<Chart.Area />
<Chart.Tooltip />
</Chart.Root>
</StatCard.Chart>The cursor and the tooltip work in a 64-pixel-tall chart exactly as they do in a full-size one, so a sparkline can still be read value by value.
The three sparkline shapes
An area for a volume that accumulates, a line for a rate, bars for counts per
period. Bars need a band axis to sit in; a line or an area wants point.
Falling numbers that are good news
<StatCard.Delta trend="down" className="text-success">
-45%
</StatCard.Delta>trend keeps the arrow pointing down while the colour says the metric is
moving the right way.
Loading and empty
Chart.Skeleton and Chart.Empty slot straight into StatCard.Chart. Pad the
slot back (px-4, plus pb-4 for the empty state) so the placeholder sits
inside the card instead of bleeding to its edges like a real chart.
Compact, for dense dashboards
size="sm" on the root tightens the padding and the gaps; the anatomy is
unchanged.
A KPI strip
API
StatCard.Root
| Prop | Type | Default | |
|---|---|---|---|
size | "default" | "sm" | — | |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus the DOM props of the element it renders. Source: stat-card/components/stat-card-root.tsx.
StatCard.Chart
Plus every <div> prop. Source: stat-card/components/stat-card-chart.tsx.
StatCard.Delta
| Prop | Type | Default | |
|---|---|---|---|
trend | "down" | "neutral" | "up" | "neutral" | Direction of the change: up renders success, down destructive,
neutral muted. Set it by what the movement means, not by its sign —
churn going up is not a success. |
Plus every <div> prop. Source: stat-card/components/stat-card-delta.tsx.
StatCard.Header
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: stat-card/components/stat-card-header.tsx.
StatCard.Label
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: stat-card/components/stat-card-label.tsx.
StatCard.Value
| Prop | Type | Default | |
|---|---|---|---|
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <div> prop. Source: stat-card/components/stat-card-value.tsx.