On this page
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
const chartData = /* ... */;
const chartConfig = /* ... */;
<>
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis
dataKey="month"
tickLine={false}
tickMargin={10}
axisLine={false}
tickFormatter={(value) => value.slice(0, 3)}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="dashed" />}
/>
<ChartLegend content={<ChartLegendContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
<Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
</BarChart>
</ChartContainer>
<ChartDataTable data={chartData} config={chartConfig} labelKey="month" />
</>Charts are a thin, themeable layer over Recharts. You write a chart with Recharts' own components — BarChart, Bar, XAxis, and the rest — and wrap them in ChartContainer, which maps your series to your design system's colors, handles light and dark theming, and makes the chart responsive. The tooltip, legend, and a visually-hidden data table are styled to match the rest of your components, so a chart looks at home next to a card or a button with no extra work.
Use the primitives directly to build any chart Recharts supports, or install a ready-made family and copy the variant you need. Browse every variant on the charts page.
Installation
The primitives — ChartContainer, ChartTooltip, ChartLegend, their *Content variants, and ChartDataTable — ship as a single chart item:
npx shadcn@latest add @dotui/chartEach family — bar, line, area, pie, radar, and radial — ships as its own item with a curated set of variants, and pulls in chart and card automatically:
npx shadcn@latest add @dotui/chart-barYou only need to add the chart item directly when you are composing a chart by hand. Installing any family brings the primitives along with it.
Usage
Import the primitives you need alongside the Recharts components for your chart type:
import { Bar, BarChart, CartesianGrid, XAxis } from 'recharts'
import {
type ChartConfig,
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from '@/components/ui/chart'A chart is built from three pieces: your data (an array of rows), a config that describes each series, and the Recharts chart wrapped in ChartContainer.
const chartData = [
{ month: 'January', desktop: 186 },
{ month: 'February', desktop: 305 },
{ month: 'March', desktop: 237 },
]
const chartConfig = {
desktop: { label: 'Desktop', color: 'var(--chart-1)' },
} satisfies ChartConfig
export function Example() {
return (
<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
<BarChart accessibilityLayer data={chartData}>
<CartesianGrid vertical={false} />
<XAxis dataKey="month" tickLine={false} axisLine={false} />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
</BarChart>
</ChartContainer>
)
}ChartContainer reads chartConfig and exposes each series key as a CSS variable — the desktop key becomes var(--color-desktop), which the Bar references with its fill. This name-keyed indirection is the heart of the API: you assign a color once in config, and every Recharts element, the tooltip, and the legend pick it up by name. There is no color array to keep aligned with your data.
Chart config
config maps each series key to a label, a color (or a per-theme theme), and an optional icon:
const chartConfig = {
desktop: { label: 'Desktop', color: 'var(--chart-1)' },
mobile: { label: 'Mobile', color: 'var(--chart-2)' },
} satisfies ChartConfigChartContainer turns each entry into a --color-<key> CSS variable you reference from Recharts (fill="var(--color-desktop)"), and the tooltip and legend read it for labels and icons. Use satisfies ChartConfig rather than a type annotation so TypeScript keeps the exact keys, which makes the matching var(--color-<key>) names easy to keep in sync.
Because config is separate from data, a key with no series of its own — an axis category, for example — can still carry a label and a color, which is how per-category coloring on pie and radial charts works.
Theming
Series colors come from a categorical palette of five slots, --chart-1 through --chart-5. Your design system derives them from its theme hues, so charts stay on-brand and adapt to light and dark automatically — no per-mode values to maintain. The palette is editable under Chart colors in the editor, and updates live as you tweak it on the charts page.
Point each series at a slot through its config color:
const chartConfig = {
desktop: { label: 'Desktop', color: 'var(--chart-1)' },
mobile: { label: 'Mobile', color: 'var(--chart-2)' },
} satisfies ChartConfigA color can be any CSS value, so you are not limited to the palette. To set a different color per mode, use theme instead of color:
const chartConfig = {
desktop: { label: 'Desktop', theme: { light: '#2563eb', dark: '#60a5fa' } },
} satisfies ChartConfigFor per-category colors — common on pie and radial charts — give each row its own fill using the same var(--color-<key>) names:
const chartData = [
{ browser: 'chrome', visitors: 275, fill: 'var(--color-chrome)' },
{ browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]Tooltip and legend
ChartTooltip and ChartLegend are the Recharts primitives; ChartTooltipContent and ChartLegendContent are the styled, config-aware contents you pass to them. Both resolve labels, colors, and icons from your config, so they stay consistent with the chart without extra wiring:
<ChartTooltip content={<ChartTooltipContent indicator="line" />} />
<ChartLegend content={<ChartLegendContent />} />ChartTooltipContent accepts an indicator of dot, line, or dashed to change the marker next to each value, plus hideLabel and hideIndicator to trim it down. ChartLegendContent accepts hideIcon and a nameKey for reading labels from a different config key. See the API reference for the full set of props.
Accessibility
Charts encode data with color and position, which is invisible to assistive technology, so accessibility is handled in two layers.
First, pass accessibilityLayer to the Recharts chart. It enables keyboard navigation and screen-reader announcements as the user moves across data points:
<BarChart accessibilityLayer data={chartData}>Second — and unique to dotUI — every family demo renders ChartDataTable alongside the chart: a visually-hidden <table> derived from the same data and config. It exposes the underlying figures to screen readers and other assistive tools without affecting the visual layout, giving you a real data-table fallback for free:
<ChartDataTable data={chartData} config={chartConfig} labelKey="month" />labelKey names the field used for each row's header — the x-axis category on cartesian charts, or the slice name on pie and radial charts. ChartDataTable adapts to your data's shape on its own: one column per series for bar, line, area, and radar; a two-column category/value table for pie and radial. Pass it the same data and config you gave ChartContainer and it stays in sync.
Families
Each family is a curated set of variants — install one, then copy the variant you need. Browse them all on the charts page.
Line
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Area
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Pie
| Browser | Visitors |
|---|---|
| Chrome | 275 |
| Safari | 200 |
| Firefox | 187 |
| Edge | 173 |
| Other | 90 |
Radar
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 273 |
| May | 209 |
| June | 214 |
Radial
| Month | Desktop | Mobile |
|---|---|---|
| january | 1,260 | 570 |
Bar
| Month | Desktop |
|---|---|
| January | 186 |
| February | 305 |
| March | 237 |
| April | 73 |
| May | 209 |
| June | 214 |
API Reference
ChartContainer
Wraps a Recharts chart with theming and a responsive container. Pass your `config` (series → label / color / icon) and a single Recharts chart child.
Supports all div attributes.
| Prop | Type | Default | |
|---|---|---|---|
ChartConfig | — | ||
{ width: number; height: number; } | { width: 320, height: 200 } | ||
ChartTooltipContent
Styled content for a chart tooltip. Use inside `<ChartTooltip content={...} />`.
Supports all div attributes.
| Prop | Type | Default | |
|---|---|---|---|
"dashed" | "dot" | "line" | "dot" | ||
boolean | false | ||
boolean | false | ||
string | — | ||
string | — | ||
ChartLegendContent
Styled content for a chart legend. Use inside `<ChartLegend content={...} />`.
Supports all div attributes.
| Prop | Type | Default | |
|---|---|---|---|
boolean | false | ||
string | — | ||
"bottom" | "top" | "bottom" | ||
ChartDataTable
A visually-hidden table that mirrors a chart's series for assistive tech (dotUI accessibility layer; not part of shadcn). Render it alongside any chart with the same props you already pass. It adapts to the data shape: wide-format (bar/line/area/radar) gets one column per series; long-format (pie/radial) gets a two-column category/value table.
Supports all table attributes.
| Prop | Type | Default | |
|---|---|---|---|
Record<string, unknown>[] | — | ||
ChartConfig | — | ||
string | — | ||
string | — | ||
string | — | ||
Last updated on 6/27/2026