Charts

Composable chart components for data visualization using Recharts

"use client";

import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart/chart";
import styles from "./chart-line-demo.module.css";

const chartData = [
  { date: 1990, canada: 27.7, uk: 57.2 },
  { date: 1991, canada: 28.0, uk: 57.4 },
  { date: 1992, canada: 28.4, uk: 57.6 },
  { date: 1993, canada: 28.7, uk: 57.7 },
  { date: 1994, canada: 29.0, uk: 57.9 },
  { date: 1995, canada: 29.3, uk: 58.0 },
  { date: 1996, canada: 29.6, uk: 58.2 },
  { date: 1997, canada: 29.9, uk: 58.3 },
  { date: 1998, canada: 30.2, uk: 58.5 },
  { date: 1999, canada: 30.4, uk: 58.7 },
  { date: 2000, canada: 30.7, uk: 59.0 },
  { date: 2001, canada: 31.0, uk: 59.1 },
  { date: 2002, canada: 31.4, uk: 59.3 },
  { date: 2003, canada: 31.6, uk: 59.6 },
  { date: 2004, canada: 31.9, uk: 60.0 },
  { date: 2005, canada: 32.2, uk: 60.4 },
  { date: 2006, canada: 32.6, uk: 60.8 },
  { date: 2007, canada: 32.9, uk: 61.3 },
  { date: 2008, canada: 33.2, uk: 61.8 },
  { date: 2009, canada: 33.6, uk: 62.3 },
  { date: 2010, canada: 34.0, uk: 62.8 },
  { date: 2011, canada: 34.3, uk: 63.3 },
  { date: 2012, canada: 34.7, uk: 63.7 },
  { date: 2013, canada: 35.1, uk: 64.1 },
  { date: 2014, canada: 35.4, uk: 64.6 },
  { date: 2015, canada: 35.7, uk: 65.1 },
  { date: 2016, canada: 36.1, uk: 65.6 },
  { date: 2017, canada: 36.5, uk: 66.0 },
  { date: 2018, canada: 37.0, uk: 66.4 },
  { date: 2019, canada: 37.6, uk: 66.8 },
  { date: 2020, canada: 38.0, uk: 67.9 },
  { date: 2021, canada: 38.2, uk: 67.0 },
  { date: 2022, canada: 38.9, uk: 67.5 },
  { date: 2023, canada: 39.6, uk: 68.3 },
  { date: 2024, canada: 40.0, uk: 69.0 },
  { date: 2025, canada: 40.1, uk: 69.8 },
];

const chartConfig = {
  canada: {
    label: "Canada",
    color: "var(--chart-1)",
  },
  uk: {
    label: "United Kingdom",
    color: "var(--chart-2)",
  },
} satisfies ChartConfig;

// biome-ignore lint/style/noMagicNumbers: X-axis tick values for 5-year intervals
const X_AXIS_TICKS = [1990, 1995, 2000, 2005, 2010, 2015, 2020, 2025];

export default function ChartLineDemo() {
  return (
    <div className={styles.container}>
      <ChartContainer className={styles.chartContainer} config={chartConfig}>
        <LineChart
          data={chartData}
          margin={{ top: 5, right: 5, left: 0, bottom: 0 }}
        >
          <CartesianGrid strokeDasharray="3 3" vertical={false} />
          <XAxis
            axisLine={false}
            dataKey="date"
            tickFormatter={(value) => value.toString()}
            tickLine={false}
            tickMargin={8}
            ticks={X_AXIS_TICKS}
          />
          <YAxis
            axisLine={false}
            domain={[0, "auto"]}
            tickLine={false}
            tickMargin={8}
            width={40}
          />
          <ChartTooltip content={<ChartTooltipContent labelKey="date" />} />
          <Line
            animationDuration={800}
            dataKey="canada"
            dot={false}
            stroke="var(--color-canada)"
            strokeWidth={2}
            type="monotone"
          />
          <Line
            animationBegin={200}
            animationDuration={800}
            dataKey="uk"
            dot={false}
            stroke="var(--color-uk)"
            strokeWidth={2}
            type="monotone"
          />
        </LineChart>
      </ChartContainer>
    </div>
  );
}

Installation

npx shadcn@latest add https://roiui.com/r/chart.json
"use client";

import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart/chart";
import styles from "./chart-bar-demo.module.css";

const chartData = [
  { month: "Jan", sales: 45.2 },
  { month: "Feb", sales: 52.8 },
  { month: "Mar", sales: 48.9 },
  { month: "Apr", sales: 67.3 },
  { month: "May", sales: 72.1 },
  { month: "Jun", sales: 89.4 },
  { month: "Jul", sales: 95.7 },
  { month: "Aug", sales: 103.2 },
];

const chartConfig = {
  sales: {
    label: "Sales",
    color: "var(--chart-1)",
  },
} satisfies ChartConfig;

// biome-ignore lint/style/noMagicNumbers: Animation duration in milliseconds
const ANIMATION_DURATION = 500;
// biome-ignore lint/style/noMagicNumbers: Border radius for top corners of bars
const BAR_RADIUS: [number, number, number, number] = [4, 4, 0, 0];

export default function ChartBarDemo() {
  return (
    <div className={styles.container}>
      <ChartContainer className={styles.chartContainer} config={chartConfig}>
        <BarChart
          data={chartData}
          margin={{ top: 5, right: 5, left: 0, bottom: 0 }}
        >
          <CartesianGrid strokeDasharray="3 3" vertical={false} />
          <XAxis
            axisLine={false}
            dataKey="month"
            tickLine={false}
            tickMargin={8}
          />
          <YAxis
            axisLine={false}
            domain={[0, "auto"]}
            tickLine={false}
            tickMargin={8}
            width={40}
          />
          <ChartTooltip
            content={
              <ChartTooltipContent
                formatter={(value) => {
                  const numValue =
                    typeof value === "number"
                      ? value
                      : Number.parseFloat(String(value));
                  return `$${numValue.toLocaleString()}k`;
                }}
              />
            }
          />
          <Bar
            animationDuration={ANIMATION_DURATION}
            dataKey="sales"
            fill="var(--color-sales)"
            radius={BAR_RADIUS}
          />
        </BarChart>
      </ChartContainer>
    </div>
  );
}
"use client";

import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart/chart";
import styles from "./chart-area-demo.module.css";

const chartData = [
  { year: 2020, revenue: 45.2 },
  { year: 2021, revenue: 52.8 },
  { year: 2022, revenue: 48.9 },
  { year: 2023, revenue: 67.3 },
  { year: 2024, revenue: 72.1 },
  { year: 2025, revenue: 89.4 },
  { year: 2026, revenue: 95.7 },
  { year: 2027, revenue: 103.2 },
  { year: 2028, revenue: 118.6 },
  { year: 2029, revenue: 127.9 },
  { year: 2030, revenue: 142.3 },
];

const chartConfig = {
  revenue: {
    label: "Revenue",
    color: "var(--chart-1)",
  },
} satisfies ChartConfig;

export default function ChartAreaDemo() {
  return (
    <div className={styles.container}>
      <ChartContainer className={styles.chartContainer} config={chartConfig}>
        <AreaChart
          data={chartData}
          margin={{ top: 5, right: 5, left: 0, bottom: 0 }}
        >
          <defs>
            <linearGradient id="fillRevenue" x1="0" x2="0" y1="0" y2="1">
              <stop
                offset="0%"
                stopColor="var(--color-revenue)"
                stopOpacity={0.3}
              />
              <stop
                offset="100%"
                stopColor="var(--color-revenue)"
                stopOpacity={0.1}
              />
            </linearGradient>
          </defs>
          <CartesianGrid strokeDasharray="3 3" vertical={false} />
          <XAxis
            axisLine={false}
            dataKey="year"
            tickFormatter={(value) => value.toString()}
            tickLine={false}
            tickMargin={8}
          />
          <YAxis
            axisLine={false}
            domain={[0, "auto"]}
            tickLine={false}
            tickMargin={8}
            width={40}
          />
          <ChartTooltip content={<ChartTooltipContent labelKey="year" />} />
          <Area
            animationDuration={800}
            dataKey="revenue"
            dot={false}
            fill="url(#fillRevenue)"
            stroke="var(--color-revenue)"
            strokeWidth={2}
            type="monotone"
          />
        </AreaChart>
      </ChartContainer>
    </div>
  );
}
"use client";

import { Pie, PieChart } from "recharts";
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
  ChartTooltipContent,
} from "@/components/ui/chart/chart";
import styles from "./chart-pie-demo.module.css";

const chartData = [
  { platform: "mobile", visitors: 45.2, fill: "var(--color-mobile)" },
  { platform: "desktop", visitors: 32.8, fill: "var(--color-desktop)" },
  { platform: "tablet", visitors: 15.9, fill: "var(--color-tablet)" },
  { platform: "smarttv", visitors: 6.1, fill: "var(--color-smarttv)" },
];

const chartConfig = {
  visitors: {
    label: "Visitors",
  },
  mobile: {
    label: "Mobile",
    color: "var(--chart-1)",
  },
  desktop: {
    label: "Desktop",
    color: "var(--chart-2)",
  },
  tablet: {
    label: "Tablet",
    color: "var(--chart-3)",
  },
  smarttv: {
    label: "Smart TV",
    color: "var(--chart-4)",
  },
} satisfies ChartConfig;

export default function ChartPieDemo() {
  return (
    <div className={styles.container}>
      <ChartContainer className={styles.chartContainer} config={chartConfig}>
        <PieChart>
          <ChartTooltip
            content={
              <ChartTooltipContent
                formatter={(value) => {
                  const numValue =
                    typeof value === "number"
                      ? value
                      : Number.parseFloat(String(value));
                  return `${numValue.toFixed(1)}%`;
                }}
                nameKey="platform"
              />
            }
          />
          <ChartLegend content={<ChartLegendContent nameKey="platform" />} />
          <Pie
            animationDuration={800}
            data={chartData}
            dataKey="visitors"
            innerRadius={60}
            nameKey="platform"
            outerRadius={100}
            stroke="transparent"
            strokeWidth={0}
          />
        </PieChart>
      </ChartContainer>
    </div>
  );
}

The main container component for all charts. Provides configuration and responsive sizing.

Component props

Prop
Type
Default

Custom tooltip content component for displaying data on hover.

Component props

Prop
Type
Default

Custom legend component for displaying chart data series.

Component props

Prop
Type
Default

For individual chart components (LineChart, BarChart, AreaChart, PieChart, etc.), refer to the Recharts API documentation.