Components

A responsive, token-themed wrapper around any Recharts chart. ChartContainer provides labels + colours from a ChartConfig and injects the --color-<key> variables; the tooltip and legend read the same config.

Install

npx shadcn@latest add @alix/chart

Preview

Usage

const config = {
  revenue: { label: "Net revenue", color: "var(--chart-1)" },
} satisfies ChartConfig;

<ChartContainer config={config} className="aspect-auto h-72">
  <AreaChart data={data}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="month" tickLine={false} axisLine={false} />
    <ChartTooltip content={<ChartTooltipContent />} />
    <Area dataKey="revenue" stroke="var(--color-revenue)" fill="var(--color-revenue)" />
  </AreaChart>
</ChartContainer>

Examples

01

Two series with a legend

Use the shared config to keep series labels, colours, tooltip rows, and legend swatches in sync.

const chartSeriesData = [
  { month: "Jan", revenue: 18.4, target: 17.8 },
  { month: "Feb", revenue: 19.7, target: 18.8 },
  { month: "Mar", revenue: 20.3, target: 20.1 },
  { month: "Apr", revenue: 21.6, target: 21.0 },
  { month: "May", revenue: 22.4, target: 22.2 },
  { month: "Jun", revenue: 23.6, target: 23.1 },
];

const chartSeriesConfig = {
  revenue: { label: "Revenue", color: "var(--chart-1)" },
  target: { label: "Target", color: "var(--chart-5)" },
} satisfies ChartConfig;

<ChartContainer config={chartSeriesConfig} className="aspect-auto h-64 w-full">
  <LineChart data={chartSeriesData} margin={{ left: 4, right: 12, top: 8, bottom: 0 }}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={8} />
    <YAxis tickLine={false} axisLine={false} tickMargin={8} width={32} />
    <ChartTooltip content={<ChartTooltipContent indicator="line" />} />
    <ChartLegend content={<ChartLegendContent />} />
    <Line dataKey="revenue" type="monotone" stroke="var(--color-revenue)" strokeWidth={2} dot={false} />
    <Line dataKey="target" type="monotone" stroke="var(--color-target)" strokeWidth={2} strokeDasharray="4 4" dot={false} />
  </LineChart>
</ChartContainer>
02

Grouped bar comparison

Bar charts work when leaders need to compare planned and delivered capacity across practices.

const chartBarData = [
  { practice: "TRS", planned: 42, delivered: 38 },
  { practice: "PI", planned: 36, delivered: 34 },
  { practice: "TIS", planned: 30, delivered: 32 },
  { practice: "ID", planned: 24, delivered: 21 },
];

const chartBarConfig = {
  planned: { label: "Planned", color: "var(--chart-5)" },
  delivered: { label: "Delivered", color: "var(--chart-1)" },
} satisfies ChartConfig;

<ChartContainer config={chartBarConfig} className="aspect-auto h-64 w-full">
  <BarChart data={chartBarData} margin={{ left: 4, right: 12, top: 8, bottom: 0 }}>
    <CartesianGrid vertical={false} />
    <XAxis dataKey="practice" tickLine={false} axisLine={false} tickMargin={8} />
    <YAxis tickLine={false} axisLine={false} tickMargin={8} width={32} />
    <ChartTooltip content={<ChartTooltipContent />} />
    <ChartLegend content={<ChartLegendContent />} />
    <Bar dataKey="planned" fill="var(--color-planned)" radius={0} />
    <Bar dataKey="delivered" fill="var(--color-delivered)" radius={0} />
  </BarChart>
</ChartContainer>
03

Inside a panel

Panel owns the dashboard frame while ChartContainer owns the responsive Recharts area.

Revenue bridge

Trailing six months, $M

On track
<Panel
  title="Revenue bridge"
  description="Trailing six months, $M"
  actions={<Badge variant="success" dot>On track</Badge>}
>
  <ChartContainer config={chartConfig} className="aspect-auto h-48 w-full">
    <AreaChart data={chartData} margin={{ left: 4, right: 8, top: 8, bottom: 0 }}>
      <defs>
        <linearGradient id="docPanelFillRevenue" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--color-revenue)" stopOpacity={0.22} />
          <stop offset="95%" stopColor="var(--color-revenue)" stopOpacity={0} />
        </linearGradient>
      </defs>
      <CartesianGrid vertical={false} />
      <XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={8} />
      <ChartTooltip content={<ChartTooltipContent />} />
      <Area dataKey="revenue" type="monotone" stroke="var(--color-revenue)" strokeWidth={2} fill="url(#docPanelFillRevenue)" />
    </AreaChart>
  </ChartContainer>
</Panel>

When to use it

Use it for

  • Executive dashboards where a metric needs axes, gridlines, tooltips, or a legend.
  • Token-coloured Recharts series that must stay consistent across the chart, tooltip, and legend.
  • Panel bodies that need a responsive chart without adding a second ResponsiveContainer.

Reach for something else

  • Tiny trend marks inside KPI cards or table cells — use Sparkline instead.
  • Tabular ledger data that users need to compare row by row — use DataTable instead.
  • Single headline metrics without an axis or interaction need — use Stat or StatCard instead.

Props

PropTypeDefaultDescription
configChartConfigMaps each series key to { label, color }. The colour is injected as --color-<key> on the chart root, so series reference var(--color-<key>) and the tooltip/legend resolve labels and swatches automatically.
childrenReactElementA single Recharts chart element (e.g. <AreaChart>). The ResponsiveContainer is supplied for you — don't add your own.
classNamestringDefaults to a 16:9 box; override with a height (e.g. "aspect-auto h-72").
ChartTooltip / ChartTooltipContentcomponentsRecharts <Tooltip> + a themed body. Use as <ChartTooltip content={<ChartTooltipContent />} />.
ChartLegend / ChartLegendContentcomponentsRecharts <Legend> + a themed body. Use as <ChartLegend content={<ChartLegendContent />} />.

See also