- Accordion
- Action Bar
- Alert Dialog
- Alert
- Announcement
- Aspect Ratio
- Autocomplete
- Avatar
- Badge
- Bottom Navigation
- Breadcrumb
- Button Group
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Circular Progress
- Circular Slider
- Clipboard
- Collapsible
- Color Picker
- Combobox
- Command
- Context Menu
- Data List
- Date Picker
- Dialog
- Drawer
- Editable
- Field
- File Upload
- Float
- Floating Panel
- Frame
- Hint
- Hover Card
- Image Cropper
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Link Overlay
- Listbox
- Marquee
- Menu
- Native Select
- Number Input
- Pagination
- Popover
- Progress
- Prose
- QR Code
- Radio Group
- Rating
- Resizable
- Scroll Area
- Segment Group
- Select
- Separator
- Sheet
- Sidebar
- Signature Pad
- Skeleton
- Skip Nav
- Slider
- Spinner
- Status
- Steps
- Switch
- Table
- Tabs
- Textarea
- Timer
- Toast
- Toggle Group
- Toggle Tooltip
- Toggle
- Tooltip
- Tour
- Tree View
For more complete, copy-ready setups (bar, line, area, pie, radar, tooltips, and more):
Browse the Charts Examples (soon).
Installation
pnpm dlx shadcn@latest add https://shark.vini.one/r/chart.json
Component
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> ) }
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
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>
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 Shark’s tokens), raw color strings in any supported format, or a mix.
CSS variables
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
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
| Prop | Type |
|---|---|
labelKey | string |
nameKey | string |
indicator | dot | line | dashed |
hideLabel | boolean |
hideIndicator | boolean |
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.