Kaapi UI
← Back to Design System

A reusable chart component built with Recharts, supporting responsive layouts, custom tooltips, legends, and dynamic gradients.

Line chart 01


import { ChartLegendContent, ChartTooltipContent } from "./base";
import { useBreakpoint } from "@/lib/hooks/use-breakpoint";
import { cn } from "@/lib/utils/cn";
import {
    Area,
    AreaChart,
    CartesianGrid,
    Label,
    Legend,
    ResponsiveContainer,
    Tooltip,
    XAxis,
    YAxis,
} from "recharts";
const lineData = [
    { date: new Date(2025, 0, 1), A: 600, B: 400, C: 100 },
    { date: new Date(2025, 1, 1), A: 620, B: 405, C: 160 },
    { date: new Date(2025, 2, 1), A: 630, B: 400, C: 170 },
    { date: new Date(2025, 3, 1), A: 650, B: 410, C: 190 },
    { date: new Date(2025, 4, 1), A: 600, B: 320, C: 200 },
    { date: new Date(2025, 5, 1), A: 650, B: 430, C: 230 },
    { date: new Date(2025, 6, 1), A: 620, B: 400, C: 200 },
    { date: new Date(2025, 7, 1), A: 750, B: 540, C: 300 },
    { date: new Date(2025, 8, 1), A: 780, B: 490, C: 390 },
    { date: new Date(2025, 9, 1), A: 750, B: 450, C: 300 },
    { date: new Date(2025, 10, 1), A: 780, B: 480, C: 340 },
    { date: new Date(2025, 11, 1), A: 820, B: 500, C: 450 },
];
export const LineChart01 = () => {
    const isDesktop = useBreakpoint("lg");
    const colors: Record<string, string> = {
        A: "text-utility-brand-600",
        B: "text-utility-brand-400",
        C: "text-utility-brand-700",
    };
    return (
        <div className="flex h-60 flex-col gap-2">
            
            <ResponsiveContainer className="h-full">
                
                <AreaChart
                    data={lineData}
                    className="text-tertiary [&_.recharts-text]:text-xs"
                    margin={{ top: isDesktop ? 12 : 6, bottom: isDesktop ? 16 : 0 }}
                >
                    
                    <defs>
                        
                        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
                            
                            <stop
                                offset="5%"
                                stopColor="currentColor"
                                className="text-utility-brand-700"
                                stopOpacity="0.7"
                            />
                            <stop
                                offset="95%"
                                stopColor="currentColor"
                                className="text-utility-brand-700"
                                stopOpacity="0"
                            />
                        </linearGradient>
                    </defs>
                    <CartesianGrid
                        vertical={false}
                        stroke="currentColor"
                        className="text-utility-gray-100"
                    />
                    <Legend
                        align="right"
                        verticalAlign="top"
                        layout={isDesktop ? "vertical" : "horizontal"}
                        content={<ChartLegendContent className="-translate-y-2" />}
                    />
                    <XAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        interval="preserveStartEnd"
                        dataKey="date"
                        tickFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short" })
                        }
                        padding={{ left: 10, right: 10 }}
                    >
                        
                        {isDesktop && (
                            <Label
                                fill="currentColor"
                                className="!text-xs font-medium max-lg:hidden"
                                position="bottom"
                            >
                                
                                Month
                            </Label>
                        )}
                    </XAxis>
                    <YAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        interval="preserveStartEnd"
                        tickFormatter={value => Number(value).toLocaleString()}
                    >
                        
                        <Label
                            value="Active users"
                            fill="currentColor"
                            className="!text-xs font-medium"
                            style={{ textAnchor: "middle" }}
                            angle={-90}
                            position="insideLeft"
                        />
                    </YAxis>
                    <Tooltip
                        content={<ChartTooltipContent />}
                        formatter={value => Number(value).toLocaleString()}
                        labelFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short", year: "numeric" })
                        }
                        cursor={{ className: "stroke-utility-brand-600 stroke-2" }}
                    />
                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["A"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="A"
                        name="Series 1"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="url(#gradient)"
                        fillOpacity={0.1}
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />
                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["B"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="B"
                        name="Series 2"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />
                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["C"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="C"
                        name="Series 3"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />
                </AreaChart>
            </ResponsiveContainer>
        </div>
    );
};
                        

Line chart 02

import { ChartLegendContent, ChartTooltipContent } from "./base";
import { useBreakpoint } from "@/lib/hooks/use-breakpoint";
import { cn } from "@/lib/utils/cn";
import {
    Area,
    AreaChart,
    CartesianGrid,
    Label,
    Legend,
    ResponsiveContainer,
    Tooltip,
    XAxis,
    YAxis,
} from "recharts";

const lineData = [
    {
        date: new Date(2025, 0, 1),
        A: 600,
        B: 400,
        C: 100,
    },
    {
        date: new Date(2025, 1, 1),
        A: 620,
        B: 405,
        C: 160,
    },
    {
        date: new Date(2025, 2, 1),
        A: 630,
        B: 400,
        C: 170,
    },
    {
        date: new Date(2025, 3, 1),
        A: 650,
        B: 410,
        C: 190,
    },
    {
        date: new Date(2025, 4, 1),
        A: 600,
        B: 320,
        C: 200,
    },
    {
        date: new Date(2025, 5, 1),
        A: 650,
        B: 430,
        C: 230,
    },
    {
        date: new Date(2025, 6, 1),
        A: 620,
        B: 400,
        C: 200,
    },
    {
        date: new Date(2025, 7, 1),
        A: 750,
        B: 540,
        C: 300,
    },
    {
        date: new Date(2025, 8, 1),
        A: 780,
        B: 490,
        C: 390,
    },
    {
        date: new Date(2025, 9, 1),
        A: 750,
        B: 450,
        C: 300,
    },
    {
        date: new Date(2025, 10, 1),
        A: 780,
        B: 480,
        C: 340,
    },
    {
        date: new Date(2025, 11, 1),
        A: 820,
        B: 500,
        C: 450,
    },
];

export const LineChart02 = () => {
    const isDesktop = useBreakpoint("lg");

    const colors: Record<string, string> = {
        A: "text-utility-brand-600",
        B: "text-utility-brand-400",
        C: "text-utility-brand-700",
    };

    return (
        <div className="flex h-60 flex-col gap-2">
            <ResponsiveContainer className="h-full">
                <AreaChart
                    data={lineData}
                    className="text-tertiary [&_.recharts-text]:text-xs"
                    margin={{
                        left: 5,
                        right: 5,
                        top: isDesktop ? 12 : 6,
                        bottom: isDesktop ? 16 : 0,
                    }}
                >
                    <defs>
                        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
                            <stop
                                offset="5%"
                                stopColor="currentColor"
                                className="text-utility-brand-700"
                                stopOpacity="0.7"
                            />
                            <stop
                                offset="95%"
                                stopColor="currentColor"
                                className="text-utility-brand-700"
                                stopOpacity="0"
                            />
                        </linearGradient>
                    </defs>

                    <CartesianGrid
                        vertical={false}
                        stroke="currentColor"
                        className="text-utility-gray-100"
                    />

                    <Legend
                        verticalAlign="top"
                        align="right"
                        layout={isDesktop ? "vertical" : "horizontal"}
                        content={<ChartLegendContent className="-translate-y-2" />}
                    />

                    <XAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        interval="preserveStartEnd"
                        dataKey="date"
                        tickFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short" })
                        }
                        padding={{ left: 10, right: 10 }}
                    >
                        {isDesktop && (
                            <Label
                                fill="currentColor"
                                className="!text-xs font-medium max-lg:hidden"
                                position="bottom"
                            >
                                Month
                            </Label>
                        )}
                    </XAxis>

                    <YAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        interval="preserveStartEnd"
                        tickFormatter={value => Number(value).toLocaleString()}
                    >
                        <Label
                            value="Active users"
                            fill="currentColor"
                            className="!text-xs font-medium"
                            style={{ textAnchor: "middle" }}
                            angle={-90}
                            position="insideLeft"
                        />
                    </YAxis>

                    <Tooltip
                        content={<ChartTooltipContent />}
                        formatter={value => Number(value).toLocaleString()}
                        labelFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short", year: "numeric" })
                        }
                        cursor={{
                            className: "stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["A"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="A"
                        name="Series 1"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="url(#gradient)"
                        fillOpacity={0.1}
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["B"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="B"
                        name="Series 2"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        strokeDasharray="0.1 8"
                        strokeLinecap="round"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["C"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="C"
                        name="Series 3"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        strokeDasharray="0.1 8"
                        strokeLinecap="round"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />
                </AreaChart>
            </ResponsiveContainer>
        </div>
    );
};
                            

Line chart 03

import { useBreakpoint } from "@/lib/hooks/use-breakpoint";
import {
    Area,
    AreaChart,
    CartesianGrid,
    Label,
    Legend,
    ResponsiveContainer,
    Tooltip,
    XAxis,
    YAxis,
} from "recharts";
import { ChartLegendContent, ChartTooltipContent } from "./base";
import { cn } from "@/lib/utils/cn";

const lineData = [
    {
        date: new Date(2025, 0, 1),
        A: 600,
        B: 400,
        C: 100,
    },
    {
        date: new Date(2025, 1, 1),
        A: 620,
        B: 405,
        C: 160,
    },
    {
        date: new Date(2025, 2, 1),
        A: 630,
        B: 400,
        C: 170,
    },
    {
        date: new Date(2025, 3, 1),
        A: 650,
        B: 410,
        C: 190,
    },
    {
        date: new Date(2025, 4, 1),
        A: 600,
        B: 320,
        C: 200,
    },
    {
        date: new Date(2025, 5, 1),
        A: 650,
        B: 430,
        C: 230,
    },
    {
        date: new Date(2025, 6, 1),
        A: 620,
        B: 400,
        C: 200,
    },
    {
        date: new Date(2025, 7, 1),
        A: 750,
        B: 540,
        C: 300,
    },
    {
        date: new Date(2025, 8, 1),
        A: 780,
        B: 490,
        C: 390,
    },
    {
        date: new Date(2025, 9, 1),
        A: 750,
        B: 450,
        C: 300,
    },
    {
        date: new Date(2025, 10, 1),
        A: 780,
        B: 480,
        C: 340,
    },
    {
        date: new Date(2025, 11, 1),
        A: 820,
        B: 500,
        C: 450,
    },
];

export const LineChart03 = () => {
    const isDesktop = useBreakpoint("lg");

    const colors: Record<string, string> = {
        A: "text-utility-brand-600",
        B: "text-utility-brand-400",
        C: "text-utility-brand-700",
    };

    return (
        <div className="flex h-60 flex-col gap-2">
            <ResponsiveContainer className="h-full">
                <AreaChart
                    data={lineData}
                    className="text-tertiary [&_.recharts-text]:text-xs"
                    margin={{
                        left: 5,
                        right: 5,
                        top: isDesktop ? 12 : 6,
                        bottom: isDesktop ? 16 : 0,
                    }}
                >
                    <defs>
                        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
                            <stop
                                offset="0%"
                                stopColor="currentColor"
                                className="text-utility-gray-500"
                                stopOpacity="0.8"
                            />
                            <stop
                                offset="80%"
                                stopColor="currentColor"
                                className="text-utility-gray-500"
                                stopOpacity="0"
                            />
                        </linearGradient>

                        <pattern
                            id="verticalLines"
                            width="8"
                            height="100%"
                            fill="url(#gradient)"
                            patternUnits="userSpaceOnUse"
                        >
                            <line
                                x1="0"
                                y1="0"
                                x2="0"
                                y2="100%"
                                stroke="currentColor"
                                className="text-utility-gray-200"
                                strokeWidth="1.5"
                            />
                            <rect
                                width="100%"
                                height="100%"
                                fill="url(#gradient)"
                                fillOpacity={0.15}
                            />
                        </pattern>
                    </defs>

                    <CartesianGrid
                        vertical={false}
                        stroke="currentColor"
                        className="text-utility-gray-100"
                    />

                    <Legend
                        verticalAlign="top"
                        align="right"
                        layout={isDesktop ? "vertical" : "horizontal"}
                        content={<ChartLegendContent className="-translate-y-2" />}
                    />

                    <XAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        tickMargin={10}
                        interval="preserveStartEnd"
                        dataKey="date"
                        padding={{ left: 10, right: 10 }}
                        tickFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short" })
                        }
                    >
                        {isDesktop && (
                            <Label
                                fill="currentColor"
                                className="!text-xs font-medium max-lg:hidden"
                                position="bottom"
                            >
                                Month
                            </Label>
                        )}
                    </XAxis>

                    <YAxis
                        fill="currentColor"
                        axisLine={false}
                        tickLine={false}
                        interval="preserveStartEnd"
                        tickFormatter={value => Number(value).toLocaleString()}
                    >
                        <Label
                            value="Active users"
                            fill="currentColor"
                            className="!text-xs font-medium"
                            style={{ textAnchor: "middle" }}
                            angle={-90}
                            position="insideLeft"
                        />
                    </YAxis>

                    <Tooltip
                        content={<ChartTooltipContent />}
                        formatter={value => Number(value).toLocaleString()}
                        labelFormatter={value =>
                            value.toLocaleDateString(undefined, { month: "short", year: "numeric" })
                        }
                        cursor={{
                            className: "stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["A"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="A"
                        name="Series 1"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="url(#verticalLines)"
                        fillOpacity={1}
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["B"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="B"
                        name="Series 2"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />

                    <Area
                        isAnimationActive={false}
                        className={cn(
                            colors["C"],
                            "[&_.recharts-area-area]:translate-y-1.5 [&_.recharts-area-area]:[clip-path:inset(0_0_6px_0)]"
                        )}
                        dataKey="C"
                        name="Series 3"
                        type="monotone"
                        stroke="currentColor"
                        strokeWidth={2}
                        fill="none"
                        activeDot={{
                            className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                        }}
                    />
                </AreaChart>
            </ResponsiveContainer>
        </div>
    );
};
                            
                            

Bar chart 01

import { useBreakpoint } from "@/lib/hooks/use-breakpoint";
import {
    Bar,
    CartesianGrid,
    Label,
    Legend,
    BarChart as RechartsBarChart,
    ResponsiveContainer,
    Tooltip,
    XAxis,
    YAxis,
} from "recharts";
import { ChartLegendContent, ChartTooltipContent } from "./base";

const barData = [
    {
        month: new Date(2025, 0, 1),
        A: 300,
        B: 200,
        C: 350,
    },
    {
        month: new Date(2025, 1, 1),
        A: 320,
        B: 300,
        C: 300,
    },
    {
        month: new Date(2025, 2, 1),
        A: 300,
        B: 200,
        C: 240,
    },
    {
        month: new Date(2025, 3, 1),
        A: 240,
        B: 300,
        C: 280,
    },
    {
        month: new Date(2025, 4, 1),
        A: 320,
        B: 280,
        C: 100,
    },
    {
        month: new Date(2025, 5, 1),
        A: 330,
        B: 300,
        C: 130,
    },
    {
        month: new Date(2025, 6, 1),
        A: 300,
        B: 200,
        C: 100,
    },
    {
        month: new Date(2025, 7, 1),
        A: 350,
        B: 300,
        C: 200,
    },
    {
        month: new Date(2025, 8, 1),
        A: 300,
        B: 200,
        C: 100,
    },
    {
        month: new Date(2025, 9, 1),
        A: 200,
        B: 300,
        C: 280,
    },
    {
        month: new Date(2025, 10, 1),
        A: 240,
        B: 300,
        C: 300,
    },
    {
        month: new Date(2025, 11, 1),
        A: 200,
        B: 400,
        C: 350,
    },
];

export const BarChart = () => {
    const isDesktop = useBreakpoint("lg");

    const colors: Record<string, string> = {
        A: "text-utility-brand-700",
        B: "text-utility-brand-500",
        C: "text-utility-gray-200",
    };

    return (
        <ResponsiveContainer className="h-60!">
            <RechartsBarChart
                data={barData}
                className="text-tertiary [&_.recharts-text]:text-xs"
                margin={{
                    left: 4,
                    right: 0,
                    top: isDesktop ? 12 : 6,
                    bottom: 18,
                }}
            >
                <CartesianGrid
                    vertical={false}
                    stroke="currentColor"
                    className="text-utility-gray-100"
                />

                <Legend
                    verticalAlign="top"
                    align="right"
                    layout={isDesktop ? "vertical" : "horizontal"}
                    content={<ChartLegendContent className="-translate-y-2" />}
                />

                <XAxis
                    fill="currentColor"
                    axisLine={false}
                    tickLine={false}
                    tickMargin={11}
                    interval="preserveStartEnd"
                    dataKey="month"
                    tickFormatter={value => value.toLocaleDateString(undefined, { month: "short" })}
                >
                    <Label
                        value="Month"
                        fill="currentColor"
                        className="!text-xs font-medium"
                        position="bottom"
                    />
                </XAxis>

                <YAxis
                    fill="currentColor"
                    axisLine={false}
                    tickLine={false}
                    interval="preserveStartEnd"
                    tickFormatter={value => Number(value).toLocaleString()}
                >
                    <Label
                        value="Active users"
                        fill="currentColor"
                        className="!text-xs font-medium"
                        style={{ textAnchor: "middle" }}
                        angle={-90}
                        position="insideLeft"
                    />
                </YAxis>

                <Tooltip
                    content={<ChartTooltipContent />}
                    formatter={value => Number(value).toLocaleString()}
                    labelFormatter={value =>
                        value.toLocaleDateString(undefined, { month: "short", year: "numeric" })
                    }
                    cursor={{
                        className: "fill-utility-gray-200/20",
                    }}
                />

                <Bar
                    isAnimationActive={false}
                    className={colors["A"]}
                    dataKey="A"
                    name="Series 1"
                    type="monotone"
                    stackId="a"
                    fill="currentColor"
                    maxBarSize={isDesktop ? 32 : 16}
                />
                <Bar
                    isAnimationActive={false}
                    className={colors["B"]}
                    dataKey="B"
                    name="Series 2"
                    type="monotone"
                    stackId="a"
                    fill="currentColor"
                    maxBarSize={isDesktop ? 32 : 16}
                />
                <Bar
                    isAnimationActive={false}
                    className={colors["C"]}
                    dataKey="C"
                    name="Series 3"
                    type="monotone"
                    stackId="a"
                    fill="currentColor"
                    maxBarSize={isDesktop ? 32 : 16}
                    radius={[6, 6, 0, 0]}
                />
            </RechartsBarChart>
        </ResponsiveContainer>
    );
};
                            

Bar chart 02

import { useBreakpoint } from "@/lib/hooks/use-breakpoint";
import {
  Bar,
  CartesianGrid,
  ComposedChart,
  Label,
  Line,
  BarChart as RechartsBarChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";
export const BarChart02 = () => {
  const colors: Record<string, string> = {
    A: "text-utility-brand-600",
    B: "text-utility-brand-400",
  };

  const data = [
    { A: 633, B: 190, date: "2025-01-01" },
    { A: 443, B: 228, date: "2025-01-08" },
    { A: 506, B: 225, date: "2025-01-15" },
    { A: 316, B: 227, date: "2025-01-22" },
    { A: 760, B: 209, date: "2025-01-29" },
    { A: 950, B: 220, date: "2025-02-05" },
    { A: 760, B: 224, date: "2025-02-12" },
    { A: 570, B: 279, date: "2025-02-19" },
    { A: 253, B: 296, date: "2025-02-26" },
    { A: 380, B: 263, date: "2025-03-05" },
    { A: 443, B: 333, date: "2025-03-12" },
    { A: 443, B: 306, date: "2025-03-19" },
    { A: 316, B: 315, date: "2025-03-26" },
    { A: 190, B: 325, date: "2025-04-02" },
    { A: 380, B: 367, date: "2025-04-09" },
    { A: 506, B: 372, date: "2025-04-16" },
    { A: 443, B: 374, date: "2025-04-23" },
    { A: 696, B: 278, date: "2025-04-30" },
    { A: 950, B: 258, date: "2025-05-07" },
    { A: 633, B: 357, date: "2025-05-14" },
    { A: 570, B: 372, date: "2025-05-21" },
    { A: 253, B: 404, date: "2025-05-28" },
    { A: 316, B: 314, date: "2025-06-04" },
    { A: 380, B: 359, date: "2025-06-11" },
    { A: 253, B: 400, date: "2025-06-18" },
    { A: 190, B: 381, date: "2025-06-25" },
    { A: 316, B: 427, date: "2025-07-02" },
    { A: 633, B: 371, date: "2025-07-09" },
    { A: 570, B: 382, date: "2025-07-16" },
    { A: 760, B: 383, date: "2025-07-23" },
    { A: 950, B: 361, date: "2025-07-30" },
    { A: 696, B: 405, date: "2025-08-06" },
    { A: 570, B: 400, date: "2025-08-13" },
    { A: 443, B: 391, date: "2025-08-20" },
    { A: 316, B: 425, date: "2025-08-27" },
    { A: 253, B: 406, date: "2025-09-03" },
    { A: 380, B: 472, date: "2025-09-10" },
    { A: 633, B: 477, date: "2025-09-17" },
    { A: 570, B: 465, date: "2025-09-24" },
    { A: 443, B: 488, date: "2025-10-01" },
    { A: 380, B: 501, date: "2025-10-08" },
    { A: 316, B: 615, date: "2025-10-15" },
    { A: 570, B: 612, date: "2025-10-22" },
    { A: 506, B: 673, date: "2025-10-29" },
    { A: 443, B: 630, date: "2025-11-05" },
    { A: 760, B: 630, date: "2025-11-12" },
    { A: 443, B: 597, date: "2025-11-19" },
    { A: 506, B: 572, date: "2025-11-26" },
    { A: 316, B: 636, date: "2025-12-03" },
    { A: 633, B: 664, date: "2025-12-10" },
    { A: 380, B: 742, date: "2025-12-17" },
    { A: 633, B: 808, date: "2025-12-24" },
  ];

  return (
    <div className="flex h-60 flex-col gap-2">
      <ResponsiveContainer className="h-full">
        <ComposedChart
          data={data}
          margin={{
            left: 4,
            right: 0,
            top: 12,
            bottom: 18,
          }}
          className="text-tertiary [&_.recharts-text]:text-xs"
        >
          <CartesianGrid
            vertical={false}
            stroke="currentColor"
            className="text-utility-gray-100"
          />

          <XAxis
            fill="currentColor"
            axisLine={false}
            tickLine={false}
            tickMargin={12}
            interval="preserveStartEnd"
            dataKey="date"
            tickFormatter={value =>
              new Date(value).toLocaleDateString(undefined, { month: "short" })
            }
            ticks={selectEvenlySpacedItems(data, 12).map(item => item.date)}
          >
            <Label
              value="Month"
              fill="currentColor"
              className="!text-xs font-medium"
              position="bottom"
            />
          </XAxis>

          <YAxis
            fill="currentColor"
            axisLine={false}
            tickLine={false}
            interval="preserveStartEnd"
            tickFormatter={value => Number(value).toLocaleString()}
          >
            <Label
              value="Active users"
              fill="currentColor"
              className="!text-xs font-medium"
              style={{ textAnchor: "middle" }}
              angle={-90}
              position="insideLeft"
            />
          </YAxis>

          <Tooltip
            content={<ChartTooltipContent />}
            formatter={value => Number(value).toLocaleString()}
            // Custom label formatter to show the week range
            labelFormatter={value => {
              const date = new Date(value);
              const endDate = new Date(date);
              endDate.setDate(date.getDate() + 6); // Set end date to 7 days after start date

              // If the start and end dates are in the same month, shorten the label (Jun 1-7, 2025)
              if (date.getMonth() === endDate.getMonth()) {
                return `${date.toLocaleDateString(undefined, { month: "long" })} ${date.getDate()}-${endDate.getDate()}, ${endDate.getFullYear()}`;
              }

              // Otherwise, show the full month range (May 30 - Jun 5, 2025)
              return `${date.toLocaleDateString(undefined, { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}`;
            }}
            cursor={{
              className: "fill-utility-gray-200/20",
            }}
          />

          <Bar
            isAnimationActive={false}
            className={colors["A"]}
            name="Mobile"
            dataKey="A"
            type="monotone"
            stackId="a"
            fill="currentColor"
            maxBarSize={12}
            radius={[4, 4, 0, 0]}
          />
          <Line
            isAnimationActive={false}
            className={colors["B"]}
            dataKey="B"
            name="Desktop"
            type="monotone"
            stroke="currentColor"
            strokeWidth={2}
            strokeDasharray="0.1 8"
            strokeLinecap="round"
            activeDot={false}
            dot={false}
          />
        </ComposedChart>
      </ResponsiveContainer>
    </div>
  );
};

Activity gauge


import {
    Legend,
    PolarAngleAxis,
    RadialBar,
    RadialBarChart,
    ResponsiveContainer,
    Tooltip,
} from "recharts";
import { ChartLegendContent, ChartTooltipContent } from "./base";
import { cn } from "@/lib/utils/cn";

const radialData = [
    {
        name: "Series 3",
        value: 660,
        className: "text-utility-brand-400",
    },
    {
        name: "Series 2",
        value: 774,
        className: "text-utility-brand-600",
    },
    {
        name: "Series 1",
        value: 866,
        className: "text-utility-brand-700",
    },
];

interface ActivityGaugeProps {
    title?: string;
    subtitle?: string;
    data?: Array<{
        name: string;
        value: number;
        className: string;
    }>;
    size?: "xs" | "sm" | "md" | "lg";
}

const sizeStyles = {
    xs: {
        height: 220,
        innerRadius: 52,
        outerRadius: 86,
        subtitledy: "-1.175em",
        titledy: "1.25em",
        subtitleFontSize: "text-xs",
        titleFontSize: "text-xl",
    },
    sm: {
        height: 268,
        innerRadius: 61,
        outerRadius: 110,
        subtitledy: "-1.35em",
        titledy: "1.15em",
        subtitleFontSize: "text-xs",
        titleFontSize: "text-display-xs",
    },
    md: {
        height: 312,
        innerRadius: 74,
        outerRadius: 132,
        subtitledy: "-1.45em",
        titledy: "1.075em",
        subtitleFontSize: "text-sm",
        titleFontSize: "text-display-sm",
    },
    lg: {
        height: 356,
        innerRadius: 84,
        outerRadius: 154,
        subtitledy: "-1.4em",
        titledy: "1em",
        subtitleFontSize: "text-sm",
        titleFontSize: "text-display-md",
    },
};

export const ActivityGauge = ({
    title = "1,000",
    subtitle = "Active users",
    data = radialData,
    size = "sm",
}: ActivityGaugeProps) => {
    return (
        <ResponsiveContainer height={sizeStyles[size].height}>
            <RadialBarChart
                data={data}
                accessibilityLayer
                innerRadius={sizeStyles[size].innerRadius}
                outerRadius={sizeStyles[size].outerRadius}
                // This is needed to start the chart at the top and go clockwise
                startAngle={90}
                endAngle={360 + 90}
                className="font-medium text-tertiary [&_.recharts-polar-grid]:text-utility-gray-100 [&_.recharts-text]:text-sm"
                margin={{
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0,
                }}
            >
                <PolarAngleAxis tick={false} domain={[0, 1000]} type="number" reversed />

                <Legend
                    verticalAlign="bottom"
                    align="center"
                    layout="horizontal"
                    content={<ChartLegendContent />}
                />

                <Tooltip content={<ChartTooltipContent isRadialChart />} />

                <RadialBar
                    isAnimationActive={false}
                    dataKey="value"
                    cornerRadius={99}
                    fill="currentColor"
                    background={{
                        className: "fill-utility-gray-100",
                    }}
                />

                {(title || subtitle) && (
                    <text x="50%" y="50%" textAnchor="middle" dominantBaseline="middle">
                        {subtitle && (
                            <tspan
                                x="50%"
                                dy={title ? sizeStyles[size].subtitledy : "1%"}
                                className={cn(
                                    "fill-current text-tertiary font-medium",
                                    sizeStyles[size].subtitleFontSize
                                )}
                            >
                                {subtitle}
                            </tspan>
                        )}
                        {title && (
                            <tspan
                                x="50%"
                                dy={subtitle ? sizeStyles[size].titledy : "1%"}
                                className={cn(
                                    "fill-current text-primary font-semibold",
                                    sizeStyles[size].titleFontSize
                                )}
                            >
                                {title}
                            </tspan>
                        )}
                    </text>
                )}
            </RadialBarChart>
        </ResponsiveContainer>
    );
};
                            

Pie


import { Legend, Pie, PieChart as RechartsPieChart, ResponsiveContainer, Tooltip } from "recharts";
import { ChartLegendContent, ChartTooltipContent } from "./base";

interface PieChartProps {
    data?: Array<{
        name: string;
        value: number;
        className: string;
    }>;
    size: "xxs" | "xs" | "sm" | "md" | "lg";
}

const pieChartData = [
    {
        name: "Series 1",
        value: 200,
        className: "text-utility-brand-600",
    },
    {
        name: "Series 2",
        value: 350,
        className: "text-utility-brand-500",
    },
    {
        name: "Series 3",
        value: 100,
        className: "text-utility-brand-400",
    },
    {
        name: "Series 4",
        value: 120,
        className: "text-utility-brand-300",
    },
    {
        name: "Series 5",
        value: 230,
        className: "text-utility-gray-200",
    },
];

const sizeStyles = {
    xxs: {
        height: 120,
        with: "max-w-52.5",
        innerRadius: 30,
        outerRadius: 60,
    },
    xs: {
        height: 160,
        with: "max-w-62.5",
        innerRadius: 40,
        outerRadius: 80,
    },
    sm: {
        height: 200,
        with: "max-w-72.5",
        innerRadius: 50,
        outerRadius: 100,
    },
    md: {
        height: 240,
        with: "max-w-96",
        innerRadius: 60,
        outerRadius: 120,
    },
    lg: {
        height: 280,
        with: "max-w-96",
        innerRadius: 70,
        outerRadius: 140,
    },
};

export const PieChart = ({ data = pieChartData, size = "sm" }: PieChartProps) => {
    return (
        <ResponsiveContainer height={sizeStyles[size].height} className={sizeStyles[size].with}>
            <RechartsPieChart
                margin={{
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0,
                }}
            >
                <Legend
                    verticalAlign="top"
                    align="right"
                    layout="vertical"
                    content={ChartLegendContent}
                />
                <Tooltip content={<ChartTooltipContent isPieChart />} />

                <Pie
                    isAnimationActive={false}
                    startAngle={-270}
                    endAngle={-630}
                    stroke="none"
                    data={data}
                    dataKey="value"
                    nameKey="name"
                    fill="currentColor"
                    innerRadius={sizeStyles[size].innerRadius}
                    outerRadius={sizeStyles[size].outerRadius}
                />
            </RechartsPieChart>
        </ResponsiveContainer>
    );
};

                            

Radar


import {
    Legend,
    PolarAngleAxis,
    PolarGrid,
    PolarRadiusAxis,
    Radar,
    RadarChart as RechartsRadarChart,
    ResponsiveContainer,
    Tooltip,
} from "recharts";
import { ChartLegendContent, ChartTooltipContent, CustomRadarChartTick } from "./base";
import { cn } from "@/lib/utils/cn";

const radarData = [
    {
        subject: "Mon",
        A: 800,
        B: 400,
        C: 600,
    },
    {
        subject: "Tue",
        A: 600,
        B: 1000,
        C: 800,
    },
    {
        subject: "Wed",
        A: 600,
        B: 200,
        C: 400,
    },
    {
        subject: "Thu",
        A: 200,
        B: 600,
        C: 800,
    },
    {
        subject: "Fri",
        A: 400,
        B: 200,
        C: 600,
    },
    {
        subject: "Sat",
        A: 1000,
        B: 800,
        C: 600,
    },
    {
        subject: "Sun",
        A: 400,
        B: 1000,
        C: 800,
    },
];

export const RadarChart = () => {
    const colors: Record<string, string> = {
        A: "text-utility-brand-600",
        B: "text-utility-pink-500",
        C: "text-utility-blue-light-500",
    };

    return (
        <ResponsiveContainer height={500} width="100%">
            <RechartsRadarChart
                cx="50%"
                cy="50%"
                outerRadius="80%"
                data={radarData}
                className="size-full font-medium text-tertiary [&_.recharts-polar-grid]:text-utility-gray-100 [&_.recharts-text]:text-sm"
                margin={{
                    left: 0,
                    right: 0,
                    top: 0,
                    bottom: 0,
                }}
            >
                <Legend
                    verticalAlign="bottom"
                    align="center"
                    layout="horizontal"
                    content={ChartLegendContent}
                />

                <PolarGrid stroke="currentColor" className="text-utility-gray-100" />
                <PolarAngleAxis
                    dataKey="subject"
                    stroke="currentColor"
                    tick={({ x, y, textAnchor, index, payload, ...props }) => (
                        <text
                            x={x}
                            y={
                                index === 0
                                    ? Number(y) - 14
                                    : index === 3 || index === 4
                                      ? Number(y) + 10
                                      : Number(y)
                            }
                            textAnchor={textAnchor}
                            {...props}
                            className={cn(
                                "recharts-text recharts-polar-angle-axis-tick-value",
                                props.className
                            )}
                        >
                            <tspan dy="0em" className="fill-utility-gray-700 text-xs font-medium">
                                {payload.value}
                            </tspan>
                        </text>
                    )}
                    tickLine={false}
                    axisLine={false}
                />
                <PolarRadiusAxis
                    textAnchor="middle"
                    tick={props => <CustomRadarChartTick {...props} />}
                    axisLine={false}
                    angle={90}
                    domain={[0, 1000]}
                />

                <Tooltip
                    content={<ChartTooltipContent />}
                    cursor={{
                        className: "stroke-utility-brand-600  stroke-2",
                        style: {
                            transform: "translateZ(0)",
                        },
                    }}
                />

                <Radar
                    isAnimationActive={false}
                    className={colors["A"]}
                    dataKey="A"
                    name="Series 1"
                    stroke="currentColor"
                    strokeWidth={2}
                    strokeLinejoin="round"
                    fill="currentColor"
                    fillOpacity={0.2}
                    activeDot={{
                        className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                    }}
                />
                <Radar
                    isAnimationActive={false}
                    className={colors["B"]}
                    dataKey="B"
                    name="Series 2"
                    stroke="currentColor"
                    strokeWidth={2}
                    strokeLinejoin="round"
                    fill="currentColor"
                    fillOpacity={0.2}
                    activeDot={{
                        className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                    }}
                />
                <Radar
                    isAnimationActive={false}
                    className={colors["C"]}
                    dataKey="C"
                    name="Series 3"
                    stroke="currentColor"
                    strokeWidth={2}
                    strokeLinejoin="round"
                    fill="currentColor"
                    fillOpacity={0.2}
                    activeDot={{
                        className: "fill-bg-primary stroke-utility-brand-600 stroke-2",
                    }}
                />
            </RechartsRadarChart>
        </ResponsiveContainer>
    );
};

                            

API Reference

Available props and utility components for chart customization.

PropTypeDescriptionDefault
dataArray<object>Data array used to render chart series.[]
ChartLegendContentReact componentCustom legend renderer supporting Tailwind variants.
ChartTooltipContentReact componentCustom tooltip renderer for improved data presentation.