Skip to content
shadcn.io
shadcn.io is not affiliated with official shadcn/ui

Shadcn Chart for React and Tailwind

Visualize data with beautiful charts.

For more complete, copy-ready setups:

Browse the Charts Examples (soon).

Installation

bunx --bun shadcn@latest add https://kit.dev/r/chart.json

Anatomy

ChartContainer
├── ChartStyle
├── Recharts surface (BarChart, LineChart, …)
├── ChartTooltip
│   └── ChartTooltipContent
└── ChartLegend
    └── ChartLegendContent

Component

These pieces follow the familiar shadcn/ui chart pattern—container, tooltips, and legend helpers around standard Recharts charts—restyled and wired to shadcn.io tokens and imports so the result feels native to this kit.

import { Bar, BarChart } from "recharts"
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"

export const MyChart = () => {
  return (
    <ChartContainer>
      <BarChart data={data}>
        <Bar dataKey="value" />
        <ChartTooltip content={(props) => <ChartTooltipContent {...props} />} />
      </BarChart>
    </ChartContainer>
  )
}

Usage

Walk through a small bar chart: sample data, a shared config object, then layers for grid, axis, tooltip, and legend.

Start with data

Shape the rows however you like; dataKey maps columns to bars, lines, or areas. This set tracks desktop versus mobile counts per month:

const chartData = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 73, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 214, mobile: 140 },
]

Add a chart config

ChartConfig is metadata: human-readable labels, optional icons, and color tokens. It stays separate from the data array so several charts can reuse the same palette and copy.

import { type ChartConfig } from "@/components/ui/chart"

const chartConfig = {
  desktop: {
    label: "Desktop",
    color: "#2563eb",
  },
  mobile: {
    label: "Mobile",
    color: "#60a5fa",
  },
} satisfies ChartConfig

Render the chart

Pass config into ChartContainer. Give the container a minimum height (for example min-h-[200px]) or a fixed height so Recharts can measure the SVG responsively.

"use client"

import { Bar, BarChart } from "recharts"
import { ChartContainer, type ChartConfig } from "@/components/ui/chart"

<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
  <BarChart accessibilityLayer data={chartData}>
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Add a grid

Import CartesianGrid from Recharts and nest it inside the chart. Horizontal stripes alone often read cleaner than a full mesh:

import { Bar, BarChart, CartesianGrid } from "recharts"

<ChartContainer config={chartConfig} className="min-h-[200px] w-full">
  <BarChart accessibilityLayer data={chartData}>
    <CartesianGrid vertical={false} />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Add an axis

Use XAxis (and YAxis when you need one) for categories and ticks. Here ticks are shortened and decorative lines are hidden for a lighter look:

import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"

<ChartContainer config={chartConfig} className="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)}
    />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Add a tooltip

shadcn.io's tooltip pair reads names, swatches, and values from chartConfig automatically:

import { ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"

<ChartContainer config={chartConfig} className="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 content={(props) => <ChartTooltipContent {...props} />} />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Add a legend

ChartLegend with ChartLegendContent mirrors the same config entries:

import { ChartLegend, ChartLegendContent } from "@/components/ui/chart"

<ChartContainer config={chartConfig} className="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 content={(props) => <ChartTooltipContent {...props} />} />
    <ChartLegend content={<ChartLegendContent />} />
    <Bar dataKey="desktop" fill="var(--color-desktop)" radius={4} />
    <Bar dataKey="mobile" fill="var(--color-mobile)" radius={4} />
  </BarChart>
</ChartContainer>

Chart config

Treat ChartConfig as the presentation contract for your series keys: labels for tooltips and legends, optional Lucide (or other) icons, and either a single color string or a theme map for light and dark. Data arrays stay focused on numbers and categories.

Because config is independent of rows, you can fetch remote data in one shape while keeping stable color tokens in code, share one config across a dashboard, or swap palettes without touching API responses.

import { Monitor } from "lucide-react"
import { type ChartConfig } from "@/components/ui/chart"

const chartConfig = {
  desktop: {
    label: "Desktop",
    icon: Monitor,
    color: "#2563eb",
    theme: {
      light: "#2563eb",
      dark: "#dc2626",
    },
  },
} satisfies ChartConfig

Use color for a single value that works in both themes, or theme with light / dark keys when the swatch should diverge.

Theming

You can theme series with CSS variables (ideal alongside shadcn.io's tokens), raw color strings in any supported format, or a mix.

CSS variables

shadcn.io's default theme already defines --chart-1 through --chart-5 in globals.css; see Styling for how those slots fit the rest of the design system. Point config entries at those variables so charts track global palette tweaks:

const chartConfig = {
  desktop: {
    label: "Desktop",
    color: "var(--chart-1)",
  },
  mobile: {
    label: "Mobile",
    color: "var(--chart-2)",
  },
} satisfies ChartConfig

Hex, HSL, or OKLCH

Inline literals work when you do not need shared tokens:

const chartConfig = {
  desktop: { label: "Desktop", color: "#2563eb" },
  mobile: { label: "Mobile", color: "hsl(220, 98%, 61%)" },
  tablet: { label: "Tablet", color: "oklch(0.5 0.2 240)" },
  laptop: { label: "Laptop", color: "var(--chart-2)" },
} satisfies ChartConfig

Applying colors in markup and data

The container exposes var(--color-<key>) for each config key. Use that placeholder anywhere Recharts or Tailwind can read a color:

  • Series elements: <Bar dataKey="desktop" fill="var(--color-desktop)" />
  • Data-driven fills: { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" } with matching config keys
  • Tailwind utilities: <LabelList className="fill-[--color-desktop]" />

Tooltip

Pair ChartTooltip with ChartTooltipContent to get a styled surface that pulls series colors and labels from chartConfig. Pass tooltip state from Recharts by using a render function for content and spreading those props into ChartTooltipContent. Toggle the label row or the color chip with props, and switch the indicator shape between dot, line, and dashed styles.

import { ChartTooltip, ChartTooltipContent } from "@/components/ui/chart"

<ChartTooltip content={(props) => <ChartTooltipContent {...props} />} />

Tooltip props

PropType
labelKeystring
nameKeystring
indicator"dot" | "line" | "dashed"
hideLabelboolean
hideIndicatorboolean

Custom label and name keys

When the tooltip should read from different fields than the default mapping, pass labelKey and nameKey so the header and each row title resolve correctly:

const chartData = [
  { browser: "chrome", visitors: 187, fill: "var(--color-chrome)" },
  { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
]

const chartConfig = {
  visitors: {
    label: "Total Visitors",
  },
  chrome: {
    label: "Chrome",
    color: "var(--chart-1)",
  },
  safari: {
    label: "Safari",
    color: "var(--chart-2)",
  },
} satisfies ChartConfig

<ChartTooltip
  content={(props) => (
    <ChartTooltipContent {...props} labelKey="visitors" nameKey="browser" />
  )}
/>

Legend

ChartLegend delegates rendering to ChartLegendContent, which builds items from the same config metadata as the tooltip.

import { ChartLegend, ChartLegendContent } from "@/components/ui/chart"

<ChartLegend content={<ChartLegendContent />} />

Custom name key

When legend entries should track a field on each datum (for example browser), set nameKey:

const chartData = [
  { browser: "chrome", visitors: 187, fill: "var(--color-chrome)" },
  { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
]

const chartConfig = {
  chrome: {
    label: "Chrome",
    color: "var(--chart-1)",
  },
  safari: {
    label: "Safari",
    color: "var(--chart-2)",
  },
} satisfies ChartConfig

<ChartLegend content={<ChartLegendContent nameKey="browser" />} />

Accessibility

Recharts’ accessibilityLayer opt-in wires keyboard traversal and screen-reader semantics into the chart canvas. Turn it on for interactive or data-critical views; leave it off for purely decorative thumbnails if you need to avoid extra focus targets.

<BarChart accessibilityLayer data={chartData}>
  {/* children */}
</BarChart>
<LineChart accessibilityLayer data={chartData}>
  {/* children */}
</LineChart>

For every prop on primitives such as Bar, Line, or XAxis, refer to the Recharts API reference.