Components
Beautiful charts built on Recharts — a container, tooltip, and legend driven by a single config and the design-system chart tokens.
Playground
ChartContainer. The config maps each data key to a label and a color; the container exposes every color as --color-{key}, which the series reference directly. Point config colors at the --chart-1…--chart-5 tokens so charts follow the theme in both modes.import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@workspace/ui/components/chart"
const chartData = [
{ month: "Jan", desktop: 186, mobile: 80 },
{ month: "Feb", desktop: 305, mobile: 200 },
{ month: "Mar", desktop: 237, mobile: 120 },
{ month: "Apr", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "Jun", desktop: 214, mobile: 140 },
]
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--chart-1)",
},
mobile: {
label: "Mobile",
color: "var(--chart-3)",
},
} satisfies ChartConfig
<ChartContainer config={chartConfig} className="w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>Line series take the color as stroke, and the tooltip switches to the line indicator to match.import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
<ChartContainer config={chartConfig} className="w-full">
<LineChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} />
<ChartTooltip content={<ChartTooltipContent indicator="line" />} />
<Line
dataKey="desktop"
type="natural"
stroke="var(--color-desktop)"
strokeWidth={2}
dot={false}
/>
<Line
dataKey="mobile"
type="natural"
stroke="var(--color-mobile)"
strokeWidth={2}
dot={false}
/>
</LineChart>
</ChartContainer>stackId to stack them, and add ChartLegend with ChartLegendContent — it reads the labels and colors from the same config as the tooltip.import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
<ChartContainer config={chartConfig} className="w-full">
<AreaChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} tickMargin={10} axisLine={false} />
<ChartTooltip content={<ChartTooltipContent indicator="dot" />} />
<ChartLegend content={<ChartLegendContent />} />
<Area
dataKey="mobile"
type="natural"
fill="var(--color-mobile)"
fillOpacity={0.4}
stroke="var(--color-mobile)"
stackId="a"
/>
<Area
dataKey="desktop"
type="natural"
fill="var(--color-desktop)"
fillOpacity={0.4}
stroke="var(--color-desktop)"
stackId="a"
/>
</AreaChart>
</ChartContainer>fill and the config keys match the values of nameKey. A donut is just innerRadius; the tooltip hides its label since each slice already names itself.import { Pie, PieChart } from "recharts"
const chartData = [
{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
{ browser: "safari", visitors: 200, fill: "var(--color-safari)" },
{ browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
{ browser: "edge", visitors: 173, fill: "var(--color-edge)" },
{ browser: "other", visitors: 90, fill: "var(--color-other)" },
]
const chartConfig = {
visitors: { label: "Visitors" },
chrome: { label: "Chrome", color: "var(--chart-1)" },
safari: { label: "Safari", color: "var(--chart-2)" },
firefox: { label: "Firefox", color: "var(--chart-3)" },
edge: { label: "Edge", color: "var(--chart-4)" },
other: { label: "Other", color: "var(--chart-5)" },
} satisfies ChartConfig
<ChartContainer config={chartConfig} className="mx-auto aspect-square h-64">
<PieChart>
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
<Pie
data={chartData}
dataKey="visitors"
nameKey="browser"
innerRadius={60}
/>
<ChartLegend content={<ChartLegendContent nameKey="browser" />} />
</PieChart>
</ChartContainer>ChartTooltip and ChartLegend re-export the Recharts Tooltip and Legend — pass the styled *Content components to their content prop. Give the container a height via aspect-* or min-h-* — it defaults to aspect-video.| Part | Key props |
|---|---|
| ChartContainer | config: ChartConfig, initialDimension |
| ChartTooltip | Recharts Tooltip — content, cursor, defaultIndex |
| ChartTooltipContent | indicator: "dot" | "line" | "dashed", hideLabel, hideIndicator, labelKey, nameKey, labelFormatter, formatter |
| ChartLegend | Recharts Legend — content, verticalAlign |
| ChartLegendContent | nameKey, hideIcon, verticalAlign |
| ChartStyle | id, config — rendered by ChartContainer |
Tokens used
11 design tokens consumed by the Chart components.
--chart-1
Chart 1
--chart-2
Chart 2
--chart-3
Chart 3
--chart-4
Chart 4
--chart-5
Chart 5
--border
Border
--background
Background
--foreground
Foreground
--muted
Muted
--muted-foreground
Muted foreground
--radius
Radius