Search for a command to run...
A collection of chart components built on Apache ECharts with SVG rendering for crisp graphics at any resolution.
Unlike shadcn's Recharts-based implementation, this chart component uses Apache ECharts with its powerful configuration-based API. The key benefits include:
ChartConfig and ChartContainer patterns from shadcnbunx shadcn@latest add @zaidan/chartYou'll also need to install the ECharts dependency:
bun add echartsThe ChartConfig type allows you to define labels, colors, and icons for each data series:
import type { ChartConfig } from "@/registry/kobalte/ui/chart"
const chartConfig: ChartConfig = { desktop: { label: "Desktop", color: "var(--chart-1)", }, mobile: { label: "Mobile", color: "var(--chart-2)", },}You can define different colors for light and dark mode:
const chartConfig: ChartConfig = { revenue: { label: "Revenue", theme: { light: "oklch(0.646 0.222 41.116)", dark: "oklch(0.488 0.243 264.376)", }, },}The component exports several preset configurations to quickly style your charts:
import { chartColors } from "@/registry/kobalte/ui/chart"
// Uses --chart-1 through --chart-5 CSS variablesconst option = { color: chartColors, // ...}import { chartTooltipDefaults } from "@/registry/kobalte/ui/chart"
const option = { tooltip: { ...chartTooltipDefaults, trigger: "axis", }, // ...}import { chartXAxisDefaults, chartYAxisDefaults } from "@/registry/kobalte/ui/chart"
const option = { xAxis: { ...chartXAxisDefaults, type: "category", data: ["Jan", "Feb", "Mar"], }, yAxis: { ...chartYAxisDefaults, type: "value", }, // ...}import { chartGridDefaults } from "@/registry/kobalte/ui/chart"
const option = { grid: chartGridDefaults, // ...}import { chartLegendDefaults } from "@/registry/kobalte/ui/chart"
const option = { legend: { ...chartLegendDefaults, data: ["Series A", "Series B"], }, // ...}Handle chart interactions using the eventHandlers prop:
<ChartContainer option={option} eventHandlers={{ click: (params) => { console.log("Clicked:", params) }, }}/>Show a loading animation while fetching data:
<ChartContainer option={option} loading={isLoading()} loadingOptions={{ text: "Loading...", }}/>Use the onInit callback to access the ECharts instance directly:
<ChartContainer option={option} onInit={(chart) => { // Access the ECharts instance chart.on("click", handleClick) }}/>const option = { color: chartColors, xAxis: { type: "category", data: categories }, yAxis: { type: "value" }, series: [{ type: "bar", data: values, itemStyle: { borderRadius: [4, 4, 0, 0] }, }],}const option = { color: chartColors, xAxis: { type: "category", data: categories, boundaryGap: false }, yAxis: { type: "value" }, series: [{ type: "line", data: values, smooth: true, }],}const option = { series: [{ type: "line", data: values, areaStyle: { opacity: 0.3 }, }],}const option = { color: chartColors, series: [{ type: "pie", radius: ["40%", "70%"], data: pieData, itemStyle: { borderRadius: 8 }, }],}const option = { color: chartColors, radar: { indicator: [ { name: "Sales", max: 100 }, { name: "Admin", max: 100 }, // ... ], }, series: [{ type: "radar", data: radarData, }],}Charts automatically adapt to light/dark mode using CSS variables. The default chart colors are defined as:
:root { --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08);}
[data-kb-theme="dark"] { --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439);}| Prop | Type | Default | Description |
|---|---|---|---|
option | EChartsOption | Required | ECharts configuration object |
config | ChartConfig | {} | Chart series configuration |
loading | boolean | false | Show loading animation |
loadingOptions | object | {} | ECharts loading options |
setOptionOpts | SetOptionOpts | { notMerge: true } | Options for setOption |
onInit | (chart: ECharts) => void | - | Callback with chart instance |
eventHandlers | Record<string, Function> | - | Chart event handlers |
class | string | - | Additional CSS classes |
type ChartConfig = { [key: string]: { label?: JSX.Element | string icon?: Component color?: string theme?: { light: string; dark: string } }}Here are the source code of all the examples from the preview page:
import type { ECBasicOption } from "echarts/types/dist/shared";import { type ChartConfig, ChartContainer, chartColors, chartGridDefaults, chartTooltipDefaults, chartXAxisDefaults, chartYAxisDefaults,} from "~/components/ui/chart";import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";function ChartAreaExample() { const option: ECBasicOption = { color: chartColors, tooltip: { ...chartTooltipDefaults, trigger: "axis" as const, }, grid: { ...chartGridDefaults, bottom: "15%", }, xAxis: { ...chartXAxisDefaults, type: "category" as const, data: monthlyData.map((d) => d.month.slice(0, 3)), boundaryGap: false, }, yAxis: { ...chartYAxisDefaults, axisLabel: { show: false, }, }, series: [ { name: "Desktop", type: "line" as const, data: monthlyData.map((d) => d.desktop), smooth: true, areaStyle: { opacity: 0.3, }, lineStyle: { width: 2, }, emphasis: { disabled: true }, }, ], };
return ( <Example title="Area Chart"> <Card class="w-full"> <CardHeader> <CardTitle>Area Chart - Stacked</CardTitle> <CardDescription>Showing total visitors for the last 6 months</CardDescription> </CardHeader> <CardContent> <ChartContainer option={option} config={chartConfig} class="h-[300px] w-full" /> </CardContent> <CardFooter> <div class="flex w-full items-start gap-2"> <div class="grid gap-2"> <div class="flex items-center gap-2 font-medium leading-none"> Trending up by 5.2% this month <TrendingUpIcon class="size-4" /> </div> <div class="flex items-center gap-2 text-muted-foreground leading-none"> January - June 2024 </div> </div> </div> </CardFooter> </Card> </Example> );}import type { ECBasicOption } from "echarts/types/dist/shared";import { type ChartConfig, ChartContainer, chartColors, chartGridDefaults, chartTooltipDefaults, chartXAxisDefaults, chartYAxisDefaults,} from "~/components/ui/chart";import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";function ChartBarExample() { const option = { color: chartColors, tooltip: { ...chartTooltipDefaults, trigger: "axis" as const, }, grid: { ...chartGridDefaults, bottom: "15%", }, xAxis: { ...chartXAxisDefaults, type: "category" as const, data: monthlyData.map((d) => d.month.slice(0, 3)), }, yAxis: { ...chartYAxisDefaults, axisLabel: { show: false, }, }, series: [ { name: "Desktop", type: "bar" as const, data: monthlyData.map((d) => d.desktop), barWidth: "40%", itemStyle: { borderRadius: [4, 4, 0, 0], }, // Disable emphasis to prevent flickering when using CSS variables // ECharts can't calculate emphasis colors from CSS variables emphasis: { disabled: true }, }, { name: "Mobile", type: "bar" as const, data: monthlyData.map((d) => d.mobile), barWidth: "40%", itemStyle: { borderRadius: [4, 4, 0, 0], }, emphasis: { disabled: true }, }, ], };
return ( <Example title="Bar Chart"> <Card class="w-full"> <CardHeader> <CardTitle>Bar Chart - Multiple</CardTitle> <CardDescription>January - June 2024</CardDescription> </CardHeader> <CardContent> <ChartContainer option={option} config={chartConfig} class="h-[300px] w-full" /> </CardContent> <CardFooter class="flex-col items-start gap-2"> <div class="flex gap-2 font-medium leading-none"> Trending up by 5.2% this month <TrendingUpIcon class="size-4" /> </div> <div class="text-muted-foreground leading-none"> Showing total visitors for the last 6 months </div> </CardFooter> </Card> </Example> );}import type { ECBasicOption } from "echarts/types/dist/shared";import { type ChartConfig, ChartContainer, chartColors, chartGridDefaults, chartLegendDefaults, chartTooltipDefaults, chartXAxisDefaults, chartYAxisDefaults,} from "~/components/ui/chart";import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";function ChartLineExample() { const option = { color: chartColors, tooltip: { ...chartTooltipDefaults, trigger: "axis" as const, }, legend: { ...chartLegendDefaults, data: ["Desktop", "Mobile"], bottom: 0, }, grid: { ...chartGridDefaults, bottom: "15%", }, xAxis: { ...chartXAxisDefaults, type: "category" as const, data: monthlyData.map((d) => d.month.slice(0, 3)), boundaryGap: false, }, yAxis: { ...chartYAxisDefaults, type: "value" as const, }, series: [ { name: "Desktop", type: "line" as const, data: monthlyData.map((d) => d.desktop), smooth: true, symbolSize: 8, lineStyle: { width: 2, }, emphasis: { disabled: true }, }, { name: "Mobile", type: "line" as const, data: monthlyData.map((d) => d.mobile), smooth: true, symbolSize: 8, lineStyle: { width: 2, }, emphasis: { disabled: true }, }, ], };
return ( <Example title="Line Chart"> <Card class="w-full"> <CardHeader> <CardTitle>Line Chart</CardTitle> <CardDescription>Showing total visitors for the last 6 months</CardDescription> </CardHeader> <CardContent> <ChartContainer option={option} config={chartConfig} class="h-[300px] w-full" /> </CardContent> </Card> </Example> );}import type { ECBasicOption } from "echarts/types/dist/shared";import { type ChartConfig, ChartContainer, chartColors, chartLegendDefaults,} from "~/components/ui/chart";import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";function ChartPieExample() { const pieData = [ { name: "Chrome", value: 275 }, { name: "Safari", value: 200 }, { name: "Firefox", value: 287 }, { name: "Edge", value: 173 }, { name: "Other", value: 190 }, ];
const pieConfig: ChartConfig = { Chrome: { label: "Chrome", color: "var(--chart-1)" }, Safari: { label: "Safari", color: "var(--chart-2)" }, Firefox: { label: "Firefox", color: "var(--chart-3)" }, Edge: { label: "Edge", color: "var(--chart-4)" }, Other: { label: "Other", color: "var(--chart-5)" }, };
const option = { color: chartColors, tooltip: { trigger: "item" as const, backgroundColor: "var(--background)", borderColor: "var(--border)", borderWidth: 1, textStyle: { color: "var(--foreground)", fontSize: 12, }, }, legend: { ...chartLegendDefaults, orient: "horizontal" as const, bottom: 0, }, series: [ { name: "Visitors", type: "pie" as const, radius: ["40%", "70%"], center: ["50%", "45%"], avoidLabelOverlap: true, itemStyle: { borderRadius: 8, borderColor: "var(--background)", borderWidth: 2, }, label: { show: false, }, // Disable emphasis to prevent flickering when using CSS variables emphasis: { disabled: true }, labelLine: { show: false, }, data: pieData, }, ], };
return ( <Example title="Pie Chart"> <Card class="w-full"> <CardHeader> <CardTitle>Pie Chart - Donut</CardTitle> <CardDescription>Browser usage for the last month</CardDescription> </CardHeader> <CardContent> <ChartContainer option={option} config={pieConfig} class="h-[300px] w-full" /> </CardContent> </Card> </Example> );}import type { ECBasicOption } from "echarts/types/dist/shared";import { ChartContainer, chartColors, chartLegendDefaults,} from "~/components/ui/chart";import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";function ChartRadarExample() { const radarIndicators = [ { name: "Sales", max: 100 }, { name: "Admin", max: 100 }, { name: "Tech", max: 100 }, { name: "Support", max: 100 }, { name: "Dev", max: 100 }, { name: "Marketing", max: 100 }, ];
const option = { color: chartColors, tooltip: { trigger: "item" as const, backgroundColor: "var(--background)", borderColor: "var(--border)", borderWidth: 1, textStyle: { color: "var(--foreground)", fontSize: 12, }, }, legend: { ...chartLegendDefaults, data: ["Team A", "Team B"], bottom: 0, }, radar: { indicator: radarIndicators, shape: "polygon" as const, axisName: { color: "var(--muted-foreground)", }, splitLine: { lineStyle: { color: "var(--border)", }, }, splitArea: { show: false, }, axisLine: { lineStyle: { color: "var(--border)", }, }, }, series: [ { type: "radar" as const, emphasis: { disabled: true }, data: [ { name: "Team A", value: [80, 50, 70, 84, 60, 55], areaStyle: { opacity: 0.3, }, }, { name: "Team B", value: [60, 75, 90, 40, 85, 70], areaStyle: { opacity: 0.3, }, }, ], }, ], };
return ( <Example title="Radar Chart"> <Card class="w-full"> <CardHeader> <CardTitle>Radar Chart</CardTitle> <CardDescription>Team performance comparison</CardDescription> </CardHeader> <CardContent> <ChartContainer option={option} class="h-[300px] w-full" /> </CardContent> </Card> </Example> );}