Docs
Installation
One command per component. The shadcn CLI copies the component and everything it imports into your project.
Requirements
- React 19 (React 18 isn't supported), in Next.js or Vite.
- A project set up for shadcn:
npx shadcn@latest initcreatescomponents.json. Custom aliases and a Tailwind prefix are respected. - Recharts 3.10 or later, or ECharts 6.1 or later. The CLI installs or updates the engine the build uses, and only that one.
- Tailwind isn't required by the charts: the theme brings its own stylesheet. If your shadcn tokens are HSL channels (the Tailwind 3 style, like
222.2 84% 4.9%), addclass="bc-hsl-tokens"to<html>.
Install
npx shadcn@latest add https://beautifulcharts.dev/r/bar-chart.jsonThe CLI copies the chart and the shared files it imports into your components folder. Installing a second chart reuses the shared files.
components/beautiful-charts/
core/ engine-free model, formatting, keyboard, sound (MIT)
themes/instrument/
styles.tsx the theme's stylesheet, injected once
frame.tsx card, header, legend, table and states
recharts/bar-chart.tsx
echarts/bar-chart.tsx (only if you install the ECharts build)
LICENSE.txtUse it
Charts are client components. Import them in a file that starts with "use client" whenever you pass a function (a formatter, a callback or tooltip content), because functions can't cross from a Server Component. The first render happens on the server, and the card reserves its height while the plot measures itself.
"use client";
import { BarChart } from "@/components/beautiful-charts/themes/instrument/recharts/bar-chart";
import type { ChartConfig } from "@/components/beautiful-charts/core/contracts";
const data = [
{ month: "Jan", signups: 186, trials: 80 },
{ month: "Feb", signups: 305, trials: 200 },
{ month: "Mar", signups: 237, trials: 120 },
{ month: "Apr", signups: 73, trials: 190 },
{ month: "May", signups: 209, trials: 130 },
{ month: "Jun", signups: 214, trials: 140 },
];
const config = {
signups: { label: "Sign-ups", color: "var(--chart-1)" },
trials: { label: "Trials", color: "var(--chart-2)" },
} satisfies ChartConfig;
export function SignupsByMonth() {
return (
<BarChart
data={data}
xKey="month"
config={config}
title="Sign-ups and trials"
description="January to June"
stack="stacked"
valueFormatter={(value) => value.toLocaleString("en-US")}
onSelectedChange={(selection) => console.log(selection?.datum.month, selection?.seriesKey)}
/>
);
}
Install names
Each component installs by its name: bar-chart, area-chart, mrr-bridge. The default build uses Recharts (or no engine, for the heatmap, KPI stat and gauge). Add -echarts for the Apache ECharts build, for example bar-chart-echarts. The longer names from before (instrument-bar-chart-recharts) still work and install the same files.
The heatmap's builds
Recharts has no heatmap, so the default build is plain SVG and needs no chart engine. For large grids (hundreds of cells and more), install the ECharts build, which draws on a canvas. Both take the same props.
npx shadcn@latest add https://beautifulcharts.dev/r/heatmap.jsonnpx shadcn@latest add https://beautifulcharts.dev/r/heatmap-echarts.jsonPro themes
Riso and Clay install from a private registry with a token from your account, once you have the pack. Set it up once per project, then install any Pro item with the command on its page. Each Pro component takes exactly the props of its Instrument version, so moving between themes changes the import path and nothing else.
// components.json (add next to what's there)
{
"registries": {
"@beautiful-charts-pro": {
"url": "https://beautifulcharts.dev/api/pro/r/{name}.json",
"headers": { "Authorization": "Bearer ${BC_PRO_TOKEN}" }
}
}
}
# .env.local (stays out of Git)
BC_PRO_TOKEN=<the token from your account>If an install fails: 401 means the token is missing or revoked, 403 means your licence doesn't include that item, 404 means the name is wrong, and 429 means too many requests in an hour.
Updating
The installed files are your source, so updating is a reviewed change, not a silent package bump. Commit first, then preview and apply:
git add -A && git commit -m "before chart update"
npx shadcn@latest add https://beautifulcharts.dev/r/bar-chart.json --dry-run
npx shadcn@latest add https://beautifulcharts.dev/r/bar-chart.json --diff components/beautiful-charts/core/cartesian.ts
npx shadcn@latest add https://beautifulcharts.dev/r/bar-chart.json --overwrite
git diff--dry-run lists what would change, --diff shows one file's changes, and --overwrite writes the new version. Charts from one release share identical copies of the shared files, so update the charts you use together.
Troubleshooting
- “Functions cannot be passed directly to Client Components”: move the chart into a file that starts with
"use client". - The chart has no height: set
height(the plot height in px), and give a grid childmin-width: 0so the chart can shrink. - All series look the same colour: check that each key in
configmatches a field in your rows. In development, the console warns about invalid colours. - The CLI can't find your alias: finish
npx shadcn@latest initfirst, socomponents.jsonexists. - The CLI asks to overwrite a shared file: you've edited it locally. Compare before accepting.
From the recipe charts
The earlier recipe charts still install from their old addresses. Moving one to the current components:
| Recipe charts | Now |
|---|---|
series={[{ key, label, color, stackId }]} | config={{ key: { label, color, stackId } }} (shadcn ChartConfig), plus series={[...keys]} to choose and order them |
showLegend, legend on/off booleans | legend={{ position, align, shape, action }} or legend={false} |
showBrush and onRangeChange({ startIndex, endIndex }) | brush={{ range, defaultRange, onRangeChange }}; ranges are indexes into your data on both engines |
onSelect({ dataIndex, datum }) or the native ECharts payload | onSelectedChange(selection): the same typed Selection on both engines |
optionOverrides (shallow) | option (deep-merged), or a function of the generated option; onReady for the instance |
isLoading | loading, plus error, and emptyState / loadingState / errorState |