Sparkline
A tiny trend line that draws itself in.
$
pnpm dlx shadcn@latest add https://lodeui.com/r/sparkline.jsonMRR
$48.2k
Active users
12,480
Churn
1.8%
components/examples/sparkline-demo.tsx
"use client";
import { Sparkline } from "@/components/ui/sparkline";
const rows = [
{ name: "MRR", value: "$48.2k", data: [30, 32, 31, 35, 38, 37, 41, 44, 43, 48] },
{ name: "Active users", value: "12,480", data: [12, 13, 12, 14, 13, 15, 16, 15, 17, 18] },
{ name: "Churn", value: "1.8%", data: [1.2, 1.3, 1.25, 1.4, 1.5, 1.45, 1.6, 1.7, 1.75, 1.8] },
];
export default function SparklineDemo() {
return (
<div className="w-full max-w-sm divide-y divide-border rounded-xl border border-border bg-card">
{rows.map((r) => (
<div key={r.name} className="flex items-center justify-between gap-4 p-4">
<div>
<p className="text-sm text-muted-foreground">{r.name}</p>
<p className="text-lg font-semibold tabular-nums">{r.value}</p>
</div>
<Sparkline data={r.data} width={110} height={32} color={r.name === "Churn" ? "var(--destructive)" : undefined} />
</div>
))}
</div>
);
}When to use
- A tiny trend next to a number in a table or KPI card.
When not to use
- When people need to read values: use an area or line chart.
Installation
$
pnpm dlx shadcn@latest add https://lodeui.com/r/sparkline.json1Install the dependencies
npm install motion2Copy the source into your project
components/ui/sparkline.tsx"use client"; import * as React from "react"; import { motion } from "motion/react"; import { cn } from "@/lib/utils"; export interface SparklineProps { data: number[]; width?: number; height?: number; /** Any CSS color. Defaults to green when the series ends higher, red when lower. */ color?: string; area?: boolean; className?: string; } export function Sparkline({ data, width = 96, height = 28, color, area = true, className }: SparklineProps) { const id = React.useId(); if (data.length < 2) return null; const min = Math.min(...data); const max = Math.max(...data); const pts = data.map((v, i) => [(i / (data.length - 1)) * width, height - 2 - ((v - min) / (max - min || 1)) * (height - 4)] as const); const line = pts.map(([x, y], i) => `${i ? "L" : "M"}${x.toFixed(1)} ${y.toFixed(1)}`).join(" "); const stroke = color ?? (data[data.length - 1] >= data[0] ? "#10b981" : "var(--destructive)"); return ( <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} className={cn("overflow-visible", className)} aria-hidden> <defs> <linearGradient id={id} x1="0" x2="0" y1="0" y2="1"> <stop offset="0%" stopColor={stroke} stopOpacity={0.25} /> <stop offset="100%" stopColor={stroke} stopOpacity={0} /> </linearGradient> </defs> {area && <path d={`${line} L${width} ${height} L0 ${height} Z`} fill={`url(#${id})`} />} <motion.path d={line} fill="none" stroke={stroke} strokeWidth={1.5} strokeLinejoin="round" strokeLinecap="round" initial={{ pathLength: 0 }} animate={{ pathLength: 1 }} transition={{ duration: 0.9, ease: [0.2, 0.8, 0.2, 1] }} /> </svg> ); }
Usage
Import it from @/components/ui/sparkline after installing.
example.tsx
import { Sparkline } from "@/components/ui/sparkline";
export function Trend() {
return <Sparkline data={[3, 5, 4, 7, 9]} width={96} height={28} />;
}API reference
Sparkline
An SVG line with optional area fill.
| Prop | Type | Default | Description |
|---|---|---|---|
| data | number[] | – | Values in order. |
| width / height | number | 96 / 28 | Size in px. |
| color | string | – | Defaults to green when the line ends higher than it starts, red otherwise. |
| area | boolean | true | Gradient fill. |
Accessibility
- Decorative (aria-hidden): put the value and change in text next to it.
Motion
- The line draws in on mount (900ms).
Responsive behavior
- Fixed size.
Performance
- One path; cheap enough for hundreds per page.
Notes for AI
- Use color='var(--destructive)' for metrics where up is bad.
This page as Markdown, for assistants and coding agents:
/components/sparkline/markdownThe full library index for assistants is at /llms.txt.
Lode222 doc pagesllms.txt