Skip to content

ComponentsDisplay

Chart

Themed building blocks for Recharts. ChartContainer makes a chart responsive, maps a config of labels, colors and icons to CSS variables that follow light and dark mode, and restyles Recharts' grid, axes and cursors with your tokens; ChartTooltipContent and ChartLegendContent render matching tooltips and legends. You compose the charts with Recharts itself.

RechartsSource

Installation

pnpm dlx shadcn@latest add @desyne/chart

The CLI installs dependencies and any other components this one uses.

Charts are built with Recharts 3, which the install adds as a dependency.

Usage

tsx
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart";
tsx
const config = {
  desktop: { label: "Desktop", color: "var(--chart-1)" },
  mobile: { label: "Mobile", color: "var(--chart-2)" },
} satisfies ChartConfig;

<ChartContainer config={config} className="h-[240px] w-full">
  <BarChart data={data}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="month" tickLine={false} axisLine={false} />
    <ChartTooltip content={<ChartTooltipContent />} />
    <ChartLegend content={<ChartLegendContent />} />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Config keys become CSS variables

Every key in config with a color or theme becomes a --color-<key> variable scoped to that chart's container, so series use fill="var(--color-desktop)". The same keys give the tooltip and legend their labels and icons. The variables only exist inside the container; for a custom legend rendered outside it, use the config's color value directly.

When to use

  • Chart: to show trends, comparisons or composition over many data points.
  • Meter or Progress Bar: a single value against a known range.
  • Card with a large number: when the one figure matters more than its shape. Add a small sparkline as in the KPI recipe.
  • Table: when users need exact values or will compare individual rows.
  • Pick the chart for the question: bars to compare categories, lines for trends over time, stacked areas for how parts add up over time, and pies or donuts only for a few parts of one whole.

Anatomy

tsx
<ChartContainer config={config}>          {/* responsive wrapper + <style> with --color-* */}
  <BarChart data={data}>                  {/* any Recharts chart */}
    <CartesianGrid />                     {/* restyled to the border token */}
    <XAxis />                             {/* tick text uses muted-foreground */}
    <ChartTooltip                         {/* = Recharts Tooltip */}
      content={<ChartTooltipContent />}   {/* themed tooltip body */}
    />
    <ChartLegend                          {/* = Recharts Legend */}
      content={<ChartLegendContent />}    {/* themed legend body */}
    />
    <Bar dataKey="desktop" fill="var(--color-desktop)" />
  </BarChart>
</ChartContainer>
PartRendersNotes
ChartContainer<div> › <style> + ResponsiveContainerProvides the config through context, writes the --color-* variables for light and .dark, and sizes the chart to the div. Defaults to aspect-video.
ChartTooltipRecharts TooltipRe-exported unchanged. Pass content={<ChartTooltipContent />}.
ChartTooltipContent<div>Label plus one row per series: indicator, config label, and the value formatted with toLocaleString().
ChartLegendRecharts LegendRe-exported unchanged. Pass content={<ChartLegendContent />}.
ChartLegendContent<div>A centered row of swatches (or config icons) with config labels.
ChartConfigtypeRecord<key, { label?, icon?, color? } or { label?, icon?, theme: { light, dark } }>.
useCharthookReturns { config } inside a ChartContainer, for custom tooltip or legend content.

Examples

Bar

Bars compare categories. Set a radius for rounded tops, hide the axis lines and use vertical={false} on the grid for a quiet background. The hero example above adds a legend below the chart.

Horizontal bar

Set layout="vertical" on BarChart, make the YAxis a category axis and the XAxis a hidden number axis. Horizontal bars suit long category labels like page paths or names.

Stacked bar

Give bars the same stackId to stack them. Round only the outer corners with a four-value radius, so the stack reads as one bar.

Area

An area chart emphasizes volume over time. A linearGradient in <defs> that fades the series color gives the fill depth; reference it with fill="url(#id)".

Stacked area

Stack areas with a shared stackId to show how parts add up to a total over time. Use a tickFormatter on the YAxis to keep large numbers short.

Line

Lines are best for trends and comparing a few series. Use dot={false} for dense data and type="monotone" for smooth curves that don't overshoot. Colors can come from any token, such as var(--destructive) for a critical series.

Pie

For a pie, put the color on each data row as fill: "var(--color-<name>)" and pass nameKey so the tooltip and legend look up each slice's label in the config. hideLabel drops the tooltip's heading, which isn't meaningful for a single slice.

Donut with a center label

innerRadius turns a pie into a donut. A Recharts Label with a content function draws the total in the middle using fill-foreground and fill-muted-foreground, so it follows the theme.

Tooltip

ChartTooltipContent shows a label and one row per series. indicator picks a dot (default), line or dashed swatch; with a single series and line or dashed, the label moves into the row. hideLabel and hideIndicator remove those parts. These previews use Recharts' defaultIndex to show the tooltip without hovering.

indicator="dot"
indicator="line"
indicator="dashed"
hideLabel

For custom content, labelFormatter(label, payload) replaces the heading and formatter(value, name, item, index) replaces an entire row. See the KPI recipe for currency formatting.

Legend and icons

ChartLegendContent renders each series' config label with a color swatch, or the config icon when one is set (the icon also replaces the tooltip indicator). Place the legend above the chart with verticalAlign="top" on both ChartLegend and ChartLegendContent.

Theming and dark mode

color accepts any CSS color, including tokens like var(--chart-1) which already switch with the theme. For colors that need a different value per mode, use theme: { light, dark }; the container writes the dark values under the .dark class. Toggle the site theme to see this chart change.

The default palette is --chart-1 to --chart-5 (indigo, teal, amber, pink, sky), defined for light and dark mode in the theme. Override those variables to re-color every chart at once.

Recipes

KPI card with sparkline

A Card with a headline figure, a trend badge and an axis-less area sparkline flush with the card's bottom edge. The tooltip uses labelFormatter and formatter to show the date and a currency value.

Monthly recurring revenue
$45,250
+18.5%

Switchable metric

A segmented ToggleButtonGroup in CardAction switches which series the bar chart shows, and the title shows the matching total. minTickGap thins the date ticks on narrow screens.

2,287 sign-ups
Last 14 days

Traffic sources

A small donut next to a custom legend list with percentages. The list sits outside ChartContainer, so its swatches use the config's color values rather than the scoped --color-* variables.

Traffic sources
36,840 visits in September
  • Search50%
  • Direct25%
  • Social16%
  • Email9%

Accessibility

  • Recharts 3 turns on its accessibilityLayer by default: the chart surface is focusable (tabIndex={0}) with role="application", and in bar, line and area charts ← and → move the tooltip between data points. Pass accessibilityLayer={false} to opt out.
  • Charts are visual. Always state the key takeaway in text nearby (a title, a total, a delta), and offer the underlying numbers as a table or download for anything important.
  • Don't rely on color alone to tell series apart. Use a legend with clear labels, direct labels, icons, or different line styles (for example strokeDasharray).
  • Keep enough contrast between adjacent series and against the background in both themes; the default --chart-* palette is tuned for this.
  • Tooltips appear on hover and keyboard focus only. Put anything essential in the visible chart or surrounding text.

Keyboard

With the default accessibility layer:

KeyAction
TabMoves focus to the chart
← / →Moves the active tooltip to the previous or next data point (axis-based charts)

Styling

Data attributes

AttributeOnPresent when
data-slot="chart"ChartContainerAlways
data-chartChartContainerAlways. A unique id (chart-<id>) that scopes the generated --color-* variables. Pass id for a stable value.

CSS variables

VariableDefined for
--color-<key>Every config key with a color or theme value, scoped to [data-chart=…], with dark values under .dark
--chart-1 … --chart-5The theme's default chart palette (global)

Avoid config keys that match Tailwind color names used inside the chart (like border or muted), since the generated --color-<key> would shadow them within the container.

Theme overrides

ChartContainer restyles Recharts' default greys to your tokens: axis tick text uses muted-foreground; the grid, polar grid, reference lines and line cursor use border; the bar cursor and radial bar backgrounds use muted. It also removes white strokes on dots and sectors and hides focus outlines on the SVG layers. Size the chart with className: the default is aspect-video; use aspect-auto h-[240px] for a fixed height, or aspect-square for pies.

API Reference

ChartContainer

Prop

Type

Also accepts every prop of <div>.

ChartConfig

Prop

Type

ChartTooltipContent

Recharts passes active, payload and label automatically when used as content.

Prop

Type

ChartLegendContent

Prop

Type

Only entries whose key is in the config get a label.

ChartTooltip / ChartLegend

Recharts' Tooltip and Legend, re-exported unchanged. Useful props include cursor, defaultIndex and content on the tooltip, and verticalAlign, align and content on the legend.

useChart

tsx
const { config } = useChart();

Reads the nearest ChartContainer's config. Throws when used outside one.

  • Card: the usual frame for a chart with a title and summary.
  • Meter: a single value within a range.
  • Toggle Button Group: switch series or time ranges.
  • Table: the accessible alternative for exact values.