# Chart

The config, axes, tooltip and legend every chart shares.

Source: [chart.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/chart.tsx)

```tsx
import { Bar, ComposedChart, Line } from "recharts";

import {
  Chart,
  ChartGrid,
  ChartLegend,
  ChartTooltip,
  ChartXAxis,
  type ChartConfig,
} from "@/components/ui/chart";

const data = [
  { month: "January", revenue: 4200, target: 4500 },
  { month: "February", revenue: 4650, target: 4500 },
  { month: "March", revenue: 5100, target: 5000 },
  { month: "April", revenue: 4980, target: 5000 },
  { month: "May", revenue: 5620, target: 5500 },
  { month: "June", revenue: 6040, target: 5500 },
];

const config = {
  revenue: { label: "Revenue" },
  target: { label: "Target", color: "var(--chart-5)" },
} satisfies ChartConfig;

export default function ChartComposed() {
  return (
    <Chart config={config} className="max-w-lg">
      <ComposedChart data={data} responsive style={{ width: "100%", height: "100%" }}>
        <ChartGrid />
        <ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
        <ChartTooltip valueFormatter={(amount) => `$${amount.toLocaleString("en-US")}`} />
        <ChartLegend />
        <Bar dataKey="revenue" fill="var(--chart-revenue)" radius={[2, 2, 0, 0]} maxBarSize={32} />
        <Line
          dataKey="target"
          type="stepAfter"
          stroke="var(--chart-target)"
          strokeWidth={1.5}
          strokeDasharray="4 3"
          dot={false}
        />
      </ComposedChart>
    </Chart>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/chart
```

The charts are built on [Recharts](https://recharts.github.io). Each chart, such as
[Area Chart](https://ui.jarv.is/components/area-chart.md), installs this file too.

## Usage

```tsx
import { Bar, BarChart } from "recharts";

import { Chart, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";

const data = [
  { month: "January", visitors: 186 },
  { month: "February", visitors: 305 },
  { month: "March", visitors: 237 },
];

const config = {
  visitors: { label: "Visitors" },
} satisfies ChartConfig;

export function VisitorsChart() {
  return (
    <Chart config={config}>
      <BarChart data={data} responsive style={{ width: "100%", height: "100%" }}>
        <ChartXAxis dataKey="month" />
        <ChartTooltip />
        <Bar dataKey="visitors" fill="var(--chart-visitors)" />
      </BarChart>
    </Chart>
  );
}
```

The [Area](https://ui.jarv.is/components/area-chart.md), [Bar](https://ui.jarv.is/components/bar-chart.md), [Line](https://ui.jarv.is/components/line-chart.md) and
[Pie](https://ui.jarv.is/components/pie-chart.md) charts already wrap `Chart`, so most of the time you only need this file's
axes, tooltip and legend. Use `Chart` directly for anything they don't cover, like the mix of bars
and a line above.

### Config

The config labels and colors each series, keyed by data key (or by sector name, for a pie).
Tooltips and legends follow its order.

```tsx
const config = {
  desktop: { label: "Desktop" },
  mobile: { label: "Mobile", color: "var(--chart-4)" },
} satisfies ChartConfig;
```

Series without a `color` cycle through the theme's `chart-1` to `chart-5`. Each key's color is
also set as `--chart-<key>`, so a Recharts series can use `var(--chart-desktop)`.

## Examples

### Icons

An `icon` takes the place of the series' swatch in the tooltip and legend.

```tsx
import { MonitorIcon, SmartphoneIcon } from "lucide-react";

import { BarChart, BarChartSeries } from "@/components/ui/bar-chart";
import { ChartLegend, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";

const data = [
  { month: "January", desktop: 186, mobile: 80 },
  { month: "February", desktop: 305, mobile: 200 },
  { month: "March", desktop: 237, mobile: 120 },
  { month: "April", desktop: 273, mobile: 190 },
  { month: "May", desktop: 209, mobile: 130 },
  { month: "June", desktop: 314, mobile: 140 },
];

const config = {
  desktop: { label: "Desktop", icon: MonitorIcon },
  mobile: { label: "Mobile", icon: SmartphoneIcon },
} satisfies ChartConfig;

export default function ChartIcons() {
  return (
    <BarChart data={data} config={config} className="max-w-lg">
      <ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
      <ChartTooltip />
      <ChartLegend />
      <BarChartSeries dataKey="desktop" />
      <BarChartSeries dataKey="mobile" />
    </BarChart>
  );
}
```

### Colors

Any CSS color works, but theme tokens adapt to dark mode.

```tsx
import { BarChart, BarChartSeries } from "@/components/ui/bar-chart";
import { ChartLegend, ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";

const data = [
  { day: "Monday", info: 12, warning: 6, critical: 1 },
  { day: "Tuesday", info: 9, warning: 4, critical: 0 },
  { day: "Wednesday", info: 14, warning: 8, critical: 3 },
  { day: "Thursday", info: 7, warning: 3, critical: 1 },
  { day: "Friday", info: 11, warning: 5, critical: 2 },
];

const config = {
  info: { label: "Info", color: "var(--info)" },
  warning: { label: "Warning", color: "var(--warning)" },
  critical: { label: "Critical", color: "var(--destructive)" },
} satisfies ChartConfig;

export default function ChartColors() {
  return (
    <BarChart data={data} config={config} stack="stacked" className="max-w-lg">
      <ChartXAxis dataKey="day" tickFormatter={(day: string) => day.slice(0, 3)} />
      <ChartTooltip />
      <ChartLegend />
      <BarChartSeries dataKey="info" />
      <BarChartSeries dataKey="warning" />
      <BarChartSeries dataKey="critical" />
    </BarChart>
  );
}
```

### Tooltip

`labelFormatter` and `valueFormatter` format the tooltip. `indicator` swaps the swatches for
lines.

```tsx
import { AreaChart, AreaChartSeries } from "@/components/ui/area-chart";
import { ChartTooltip, ChartXAxis, type ChartConfig } from "@/components/ui/chart";

const data = [
  { date: "2026-03-02", downloads: 1240 },
  { date: "2026-03-03", downloads: 1380 },
  { date: "2026-03-04", downloads: 1310 },
  { date: "2026-03-05", downloads: 1520 },
  { date: "2026-03-06", downloads: 1610 },
  { date: "2026-03-07", downloads: 980 },
  { date: "2026-03-08", downloads: 870 },
];

const config = {
  downloads: { label: "Downloads" },
} satisfies ChartConfig;

const weekday = new Intl.DateTimeFormat("en-US", { weekday: "short", timeZone: "UTC" });
const fullDate = new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeZone: "UTC" });

export default function ChartTooltipFormat() {
  return (
    <AreaChart data={data} config={config} className="max-w-lg">
      <ChartXAxis dataKey="date" tickFormatter={(date: string) => weekday.format(new Date(date))} />
      <ChartTooltip
        indicator="line"
        labelFormatter={(date) => fullDate.format(new Date(String(date)))}
        valueFormatter={(downloads) => downloads.toLocaleString("en-US")}
      />
      <AreaChartSeries dataKey="downloads" />
    </AreaChart>
  );
}
```

### Legend

`align` and `verticalAlign` position the legend.

```tsx
import { ChartGrid, ChartLegend, ChartXAxis, type ChartConfig } from "@/components/ui/chart";
import { LineChart, LineChartSeries } from "@/components/ui/line-chart";

const data = [
  { month: "January", north: 186, south: 120, west: 94 },
  { month: "February", north: 205, south: 138, west: 112 },
  { month: "March", north: 237, south: 152, west: 108 },
  { month: "April", north: 228, south: 171, west: 131 },
  { month: "May", north: 259, south: 166, west: 147 },
  { month: "June", north: 284, south: 189, west: 158 },
];

const config = {
  north: { label: "North" },
  south: { label: "South" },
  west: { label: "West" },
} satisfies ChartConfig;

export default function ChartLegendBottom() {
  return (
    <LineChart data={data} config={config} className="max-w-lg">
      <ChartGrid />
      <ChartXAxis dataKey="month" tickFormatter={(month: string) => month.slice(0, 3)} />
      <ChartLegend align="center" verticalAlign="bottom" />
      <LineChartSeries dataKey="north" />
      <LineChartSeries dataKey="south" />
      <LineChartSeries dataKey="west" />
    </LineChart>
  );
}
```

## Props

| Part | Extends |
| --- | --- |
| `Chart` | `<div>` |
| `ChartGrid` | `CartesianGrid` |
| `ChartXAxis` | `XAxis` |
| `ChartYAxis` | `YAxis` |
| `ChartTooltip` | `Tooltip` |
| `ChartLegend` | `Legend` |

### Chart

| Prop     | Type          | Description                                                |
| -------- | ------------- | ---------------------------------------------------------- |
| `config` | `ChartConfig` | Each series' label, icon and color, keyed by its data key. |

### ChartTooltip

| Prop              | Type                                                          | Description                                                                          |
| ----------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `indicator?`      | `"dashed" \| "dot" \| "line"`                                 | The swatch beside each series: a square, or a solid or dashed line. Default: `"dot"` |
| `hideLabel?`      | `boolean`                                                     | Leaves out the label above the values, such as the hovered month. Default: `false`   |
| `hideIndicator?`  | `boolean`                                                     | Leaves out the swatches. A series with an icon still shows it. Default: `false`      |
| `valueFormatter?` | `((value: number \| string, key: string) => React.ReactNode)` | Formats each value, as a price or with its unit, say.                                |

### ChartLegend

| Prop        | Type      | Description                                                                |
| ----------- | --------- | -------------------------------------------------------------------------- |
| `hideIcon?` | `boolean` | Draws swatches for every series, even those with an icon. Default: `false` |

Each part also takes the props of the Recharts component it extends.
