Data display
Stat
Key figure with change against the last period and a trend.
Conversations this week
2,431
+12.4%vs last week
Median time to cause
4h 12m
-31%vs last week
Repeat contacts
8.2%
+2.1%vs last week
pnpm dlx shadcn@latest add @paragon/statBrings in@paragon/sparklinelucide-reactcn
registry/paragon/components/stat.tsx
import * as React from "react"
import { TrendingDown, TrendingUp } from "lucide-react"
import { cn } from "cn"
import { Sparkline } from "@/registry/paragon/components/sparkline"
/**
* A key figure with its change and trend. `delta` is a fraction (0.124 = +12.4%).
* Set `good="down"` for metrics where falling is the win, such as
* resolution time or repeat contacts.
*/
function Stat({
label,
value,
delta,
deltaLabel = "vs last week",
good = "up",
trend,
locale = "en-GB",
className,
...props
}: Omit<React.ComponentProps<"div">, "children"> & {
label: React.ReactNode
value: React.ReactNode
delta?: number
deltaLabel?: React.ReactNode
good?: "up" | "down"
trend?: number[]
locale?: string
}) {
const direction = delta == null || delta === 0 ? "flat" : delta > 0 ? "up" : "down"
const outcome = direction === "flat" ? "neutral" : direction === good ? "good" : "bad"
const Icon = direction === "down" ? TrendingDown : TrendingUp
return (
<div
data-slot="stat"
className={cn("flex flex-col gap-4 rounded-card bg-card p-5 ring-1 ring-border", className)}
{...props}
>
<p className="text-sm text-muted-foreground">{label}</p>
<div className="flex items-end justify-between gap-4">
<p className="text-[2rem] leading-none font-medium tracking-[-0.04em] whitespace-nowrap tabular-nums">{value}</p>
{trend ? (
<Sparkline
values={trend}
area
className={cn(
"h-9 w-24 min-w-10 shrink",
outcome === "good" && "text-oasis dark:text-[#a8cf9f]",
outcome === "bad" && "text-destructive",
outcome === "neutral" && "text-foreground/50"
)}
/>
) : null}
</div>
{delta != null ? (
<p className="flex items-center gap-1.5 text-sm text-muted-foreground">
<span
data-outcome={outcome}
className="inline-flex items-center gap-1 font-medium tabular-nums data-[outcome=bad]:text-destructive data-[outcome=good]:text-oasis data-[outcome=neutral]:text-muted-foreground dark:data-[outcome=good]:text-[#a8cf9f]"
>
{direction !== "flat" ? <Icon aria-hidden="true" className="size-3.5 rtl:-scale-x-100" /> : null}
{new Intl.NumberFormat(locale, {
style: "percent",
maximumFractionDigits: 1,
signDisplay: "exceptZero",
}).format(delta)}
</span>
{deltaLabel}
</p>
) : null}
</div>
)
}
export { Stat }