docs/components/chart
Sections
Components
Utilities
Forms
Hooks
Copy Markdown
Visualize data with beautiful charts.
PreviewCode
For more complete, copy-ready setups:
Browse the Charts Examples (soon).
CLIManual
pnpmbunnpmyarn
pnpm dlx shadcn@latest add @shark/chart
ChartContainer
├── ChartStyle
├── Recharts surface (BarChart, LineChart, …)
├── ChartTooltip
│ └── ChartTooltipContent
└── ChartLegend
└── ChartLegendContent
These pieces follow the familiar shadcn/ui chart pattern—container, tooltips, and legend helpers around standard Recharts charts—restyled and wired to Shark UI 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>
)
}
Walk through a small bar chart: sample data, a shared config object, then layers for grid, axis, tooltip, and legend.
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 },
]
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
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>
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>
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>
Shark’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>
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>
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.
You can theme series with CSS variables (ideal alongside Shark’s tokens), raw color strings in any supported format, or a mix.
Shark’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
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
The container exposes var(--color-<key>) for each config key. Use that placeholder anywhere Recharts or Tailwind can read a color:
<Bar dataKey="desktop" fill="var(--color-desktop)" />{ browser: "chrome", visitors: 275, fill: "var(--color-chrome)" } with matching config keys<LabelList className="fill-[--color-desktop]" />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} />} />
| Prop | Type |
|---|---|
labelKey | string |
nameKey | string |
indicator | `"dot" |
hideLabel | boolean |
hideIndicator | boolean |
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" />
)}
/>
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 />} />
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" />} />
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}>
</BarChart>
<LineChart accessibilityLayer data={chartData}>
</LineChart>
For every prop on primitives such as Bar, Line, or XAxis, refer to the Recharts API reference.
[
Previous page
Carousel ](/docs/components/carousel)[
Next page
Checkbox ](/docs/components/checkbox)
On This Page
InstallationAnatomyComponentUsageStart with dataAdd a chart configRender the chartAdd a gridAdd an axisAdd a tooltipAdd a legendChart configThemingCSS variablesHex, HSL, or OKLCHApplying colors in markup and dataTooltipTooltip propsCustom label and name keysLegendCustom name keyAccessibility