import { Bar, ComposedChart, Line } from "recharts";
import {
Chart,
ChartGrid,
ChartLegend,
ChartTooltip,
ChartXAxis,
type ChartConfig,
} from "@/components/ui/chart";
const data = [
{ month: "January", revenue: 4200, target: 4500 },
{ month: "February", revenue: 4650, target: 4500 },
{ month: "March", revenue: 5100, target: 5000 },
{ month: "April", revenue: 4980, target: 5000 },
{ month: "May", revenue: 5620, target: 5500 },
{ month: "June", revenue: 6040, target: 5500 },
];
const config = {
revenue: { label: "Revenue" },
target: { label: "Target", color: "var(--chart-5)" },
} satisfies ChartConfig;
export default function ChartComposed() {
return (
<Chart config={config} className="max-w-lg">
<ComposedChart data={data} responsive style={{ width: "100%", height: "100%" }}>
<ChartGrid />
<ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
<ChartTooltip valueFormatter={(amount) => `$${amount.toLocaleString("en-US")}`} />
<ChartLegend />
<Bar dataKey="revenue" fill="var(--chart-revenue)" radius={[2, 2, 0, 0]} maxBarSize={32} />
<Line
dataKey="target"
type="stepAfter"
stroke="var(--chart-target)"
strokeWidth={1.5}
strokeDasharray="4 3"
dot={false}
/>
</ComposedChart>
</Chart>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add chart
pnpm dlx shadcn@latest add @jarvis/chart
The charts are built on Recharts. Each chart, such as Area Chart, installs this file too.
Usage
import { Bar, BarChart } from "recharts";
import { Chart, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
const data = [
{ month: "January", visitors: 186 },
{ month: "February", visitors: 305 },
{ month: "March", visitors: 237 },
];
const config = {
visitors: { label: "Visitors" },
} satisfies ChartConfig;
export function VisitorsChart() {
return (
<Chart config={config}>
<BarChart data={data} responsive style={{ width: "100%", height: "100%" }}>
<ChartXAxis dataKey="month" />
<ChartTooltip />
<Bar dataKey="visitors" fill="var(--chart-visitors)" />
</BarChart>
</Chart>
);
}The Area, Bar, Line and
Pie charts already wrap Chart, so most of the time you only need this file's
axes, tooltip and legend. Use Chart directly for anything they don't cover, like the mix of bars
and a line above.
Config
The config labels and colors each series, keyed by data key (or by sector name, for a pie). Tooltips and legends follow its order.
const config = {
desktop: { label: "Desktop" },
mobile: { label: "Mobile", color: "var(--chart-4)" },
} satisfies ChartConfig;Series without a color cycle through the theme's chart-1 to chart-5. Each key's color is
also set as --chart-<key>, so a Recharts series can use var(--chart-desktop).
Examples
Icons
An icon takes the place of the series' swatch in the tooltip and legend.
import { MonitorIcon, SmartphoneIcon } from "lucide-react";
import { BarChart, BarChartSeries } from "@/components/ui/bar-chart";
import { ChartLegend, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
const data = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 273, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 314, mobile: 140 },
];
const config = {
desktop: { label: "Desktop", icon: MonitorIcon },
mobile: { label: "Mobile", icon: SmartphoneIcon },
} satisfies ChartConfig;
export default function ChartIcons() {
return (
<BarChart data={data} config={config} className="max-w-lg">
<ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
<ChartTooltip />
<ChartLegend />
<BarChartSeries dataKey="desktop" />
<BarChartSeries dataKey="mobile" />
</BarChart>
);
}Colors
Any CSS color works, but theme tokens adapt to dark mode.
import { BarChart, BarChartSeries } from "@/components/ui/bar-chart";
import { ChartLegend, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
const data = [
{ day: "Monday", info: 12, warning: 6, critical: 1 },
{ day: "Tuesday", info: 9, warning: 4, critical: 0 },
{ day: "Wednesday", info: 14, warning: 8, critical: 3 },
{ day: "Thursday", info: 7, warning: 3, critical: 1 },
{ day: "Friday", info: 11, warning: 5, critical: 2 },
];
const config = {
info: { label: "Info", color: "var(--info)" },
warning: { label: "Warning", color: "var(--warning)" },
critical: { label: "Critical", color: "var(--destructive)" },
} satisfies ChartConfig;
export default function ChartColors() {
return (
<BarChart data={data} config={config} stack="stacked" className="max-w-lg">
<ChartXAxis dataKey="day" tickFormatter={(day: string) => day.slice(0, 3)} />
<ChartTooltip />
<ChartLegend />
<BarChartSeries dataKey="info" />
<BarChartSeries dataKey="warning" />
<BarChartSeries dataKey="critical" />
</BarChart>
);
}Tooltip
labelFormatter and valueFormatter format the tooltip. indicator swaps the swatches for
lines.
import { AreaChart, AreaChartSeries } from "@/components/ui/area-chart";
import { ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
const data = [
{ date: "2026-03-02", downloads: 1240 },
{ date: "2026-03-03", downloads: 1380 },
{ date: "2026-03-04", downloads: 1310 },
{ date: "2026-03-05", downloads: 1520 },
{ date: "2026-03-06", downloads: 1610 },
{ date: "2026-03-07", downloads: 980 },
{ date: "2026-03-08", downloads: 870 },
];
const config = {
downloads: { label: "Downloads" },
} satisfies ChartConfig;
const weekday = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" });
const fullDate = new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeZone: "UTC" });
export default function ChartTooltipFormat() {
return (
<AreaChart data={data} config={config} className="max-w-lg">
<ChartXAxis dataKey="date" tickFormatter={(date: string) => weekday.format(new Date(date))} />
<ChartTooltip
indicator="line"
labelFormatter={(date) => fullDate.format(new Date(String(date)))}
valueFormatter={(downloads) => downloads.toLocaleString("en-US")}
/>
<AreaChartSeries dataKey="downloads" />
</AreaChart>
);
}Legend
align and verticalAlign position the legend.
import { ChartGrid, ChartLegend, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
import { LineChart, LineChartSeries } from "@/components/ui/line-chart";
const data = [
{ month: "January", north: 186, south: 120, west: 94 },
{ month: "February", north: 205, south: 138, west: 112 },
{ month: "March", north: 237, south: 152, west: 108 },
{ month: "April", north: 228, south: 171, west: 131 },
{ month: "May", north: 259, south: 166, west: 147 },
{ month: "June", north: 284, south: 189, west: 158 },
];
const config = {
north: { label: "North" },
south: { label: "South" },
west: { label: "West" },
} satisfies ChartConfig;
export default function ChartLegendBottom() {
return (
<LineChart data={data} config={config} className="max-w-lg">
<ChartGrid />
<ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
<ChartLegend align="center" verticalAlign="bottom" />
<LineChartSeries dataKey="north" />
<LineChartSeries dataKey="south" />
<LineChartSeries dataKey="west" />
</LineChart>
);
}Props
| Part | Extends |
|---|---|
| Chart | <div> |
| ChartGrid | CartesianGrid |
| ChartXAxis | XAxis |
| ChartYAxis | YAxis |
| ChartTooltip | Tooltip |
| ChartLegend | Legend |
Chart
| Prop | Type | Default |
|---|---|---|
configEach series' label, icon and color, keyed by its data key. | ChartConfig | — |
ChartTooltip
| Prop | Type | Default |
|---|---|---|
indicatorThe swatch beside each series: a square, or a solid or dashed line. | "dashed" | "dot" | "line" | "dot" |
hideLabelLeaves out the label above the values, such as the hovered month. | boolean | false |
hideIndicatorLeaves out the swatches. A series with an icon still shows it. | boolean | false |
valueFormatterFormats each value, as a price or with its unit, say. | ((value: number | string, key: string) => React.ReactNode) | — |
ChartLegend
| Prop | Type | Default |
|---|---|---|
hideIconDraws swatches for every series, even those with an icon. | boolean | false |
Each part also takes the props of the Recharts component it extends.