A metric card with a clear comparison trend, optional visual, and readable value hierarchy.
import { StatCard } from "@/components/ballmac/stat-card"
import { Sparkline } from "@/components/ballmac/sparkline"
export default function StatCardDemo() {
return (
<StatCard
className="w-full max-w-sm"
label="Monthly active workspaces"
value="12,840"
change={12.8}
comparison="from last month"
visual={
<Sparkline
className="w-24"
values={[6, 8, 7, 11, 10, 14, 16]}
label="Workspaces"
/>
}
/>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/stat-cardInstall the dependencies.
$ pnpm add lucide-reactCopy the source into your project.
components/ballmac/stat-card.tsx// Ballmac UI: Stat Card. https://ui.ballmac.com/components/stat-card import * as React from "react" import { ArrowDownRight, ArrowUpRight, Minus } from "lucide-react" import { cn } from "@/lib/utils" type StatCardProps = React.ComponentProps<"div"> & { /** Short metric label. */ label: string /** Formatted value, including any unit or currency symbol. */ value: React.ReactNode /** Signed percentage change from the comparison period. */ change?: number /** Description of the comparison period. */ comparison?: string /** Whether a smaller value is the desired outcome. */ lowerIsBetter?: boolean /** Optional small chart or icon at the right. */ visual?: React.ReactNode } function StatCard({ className, label, value, change, comparison, lowerIsBetter = false, visual, ...props }: StatCardProps) { const direction = change === undefined || change === 0 ? "neutral" : (change > 0) !== lowerIsBetter ? "positive" : "negative" const Icon = change === undefined || change === 0 ? Minus : change > 0 ? ArrowUpRight : ArrowDownRight return ( <div data-slot="stat-card" className={cn( "bg-card text-card-foreground flex min-w-0 flex-col gap-4 rounded-xl border border-border p-5 shadow-sm", className, )} {...props} > <div className="flex items-start justify-between gap-3"> <span data-slot="stat-card-label" className="text-muted-foreground text-sm font-medium" > {label} </span> {visual && ( <span data-slot="stat-card-visual" aria-hidden="true" className="text-primary shrink-0" > {visual} </span> )} </div> <div data-slot="stat-card-value" className="truncate text-3xl font-semibold tracking-tight tabular-nums" > {value} </div> {(change !== undefined || comparison) && ( <div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs"> {change !== undefined && ( <span data-slot="stat-card-change" data-trend={direction} className={cn( "relative inline-flex items-center gap-0.5 rounded-full px-2 py-1 font-medium tabular-nums", direction === "positive" ? "bg-primary/10 text-primary" : direction === "negative" ? "bg-destructive/10 text-foreground" : "bg-muted text-muted-foreground", )} > <Icon aria-hidden="true" className="size-3.5" /> <span className="sr-only">{direction === "positive" ? "Improving" : direction === "negative" ? "Worsening" : "Unchanged"}: </span> {Math.abs(change)}% </span> )} {comparison && ( <span className="text-muted-foreground">{comparison}</span> )} </div> )} </div> ) } export { StatCard, type StatCardProps }Update the import paths to match your project setup.
Usage
import { StatCard } from "@/components/ballmac/stat-card"The full example is in the Code tab above.
Examples
States and variants
import { StatCard } from "@/components/ballmac/stat-card"
export default function StatCardStates() {
return (
<div className="grid w-full max-w-md gap-3 sm:grid-cols-2">
<StatCard
label="Response time"
value="84 ms"
change={-18}
lowerIsBetter
comparison="versus last week"
/>
<StatCard
label="Open issues"
value="7"
change={3}
lowerIsBetter
comparison="versus last week"
/>
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
label*Short metric label. | string | — |
value*Formatted value, including any unit or currency symbol. | React.ReactNode | — |
changeSigned percentage change from the comparison period. | number | — |
comparisonDescription of the comparison period. | string | — |
lowerIsBetterWhether a smaller value is the desired outcome. | boolean | false |
visualOptional small chart or icon at the right. | React.ReactNode | — |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| None | Static content is announced with semantic structure |
Use with AI
A metric card with a clear comparison trend, optional visual, and readable value hierarchy. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Stat Card (@ballmac/stat-card) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Track a key metric with a comparison period
Not for
- Use Kpi Row for several compact metrics
Registry JSON: https://ui.ballmac.com/r/stat-card.json
Credits
Free to use in personal and commercial projects.
- npm
- lucide-react
- Registry
- shadcn/utils
Pairs well with
Card
A composable content surface with compact spacing, an action slot, and optional interactive feedback.
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.
Avatar Circles
Overlapping avatars that spread on hover, lift individually with a name and role tooltip, show presence, fall back to initials and end in a +N count that can be a button.