---
title: "Chart"
description: "A chart to visualize data as lines, areas, bars or a donut."
canonical_url: "https://vinicunca.dev/pohon/components/chart"
---
# Chart

> A chart to visualize data as lines, areas, bars or a donut.

## Usage

The Chart component is built on top of [TanStack Charts](https://tanstack.com/charts/latest) and renders an SVG on the server.

TanStack Charts is an optional peer dependency, install it first:

```bash [pnpm]
pnpm add @tanstack/charts
```

```bash [yarn]
yarn add @tanstack/charts
```

```bash [npm]
npm install @tanstack/charts
```

```bash [bun]
bun add @tanstack/charts
```

### Data

Use the `data` prop to pass an array of objects, the `index` prop to set the key used for the x axis and the `categories` prop to set the series, each with the `key` of its values and an optional `label` and `color`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

> [!NOTE]
> 
> You can also pass categories as strings, like `['desktop', 'mobile']`, when they read well as names.

> [!TIP]
> 
> Use the `aria-label` prop to name the chart for assistive technologies.

### Type

Use the `type` prop to change the type of chart. Defaults to `line`.

> [!NOTE]
> 
> With `donut`, each row of `data` is a slice. The `index` sets its label and the first key of `categories` its value.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart type="donut" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Stacked

Use the `stacked` prop to stack the series on top of each other.

> [!WARNING]
> 
> This prop only works when `type` is `area` or `bar`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart type="bar" stacked :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Curve

Use the `curve` prop to change the shape of the line between values. Defaults to `linear`.

> [!WARNING]
> 
> This prop only works when `type` is `line` or `area`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart type="line" curve="monotone" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Colors

Use the `colors` prop to set the color of each series with theme colors or any CSS color.

> [!NOTE]
> 
> The colors follow the `categories` order, or the `data` order with `donut`. A `color` set on a category takes precedence. When there are more series than colors, they repeat as lighter tints.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :colors="[
  'success',
  '#8b5cf6'
]" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

> [!TIP]
> See: /pohon/getting-started/theme/css-variables#chart
> 
> The colors default to the `--ui-chart-*` CSS variables, which you can customize in your `main.css` file.

### Points

Use the `points` prop to draw a dot at each value.

> [!WARNING]
> 
> This prop only works when `type` is `line`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart points :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Grid

Use the `grid` prop to toggle the horizontal grid lines. Defaults to `true`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :grid="false" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Axis

Use the `x-axis` and `y-axis` props to toggle each axis. Both default to `true`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :x-axis="false" :y-axis="false" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Axis labels

Use the `x-label` and `y-label` props to display a title along each axis.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart x-label="Month" y-label="Visitors" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Legend

Use the `legend` prop to toggle the legend.

> [!NOTE]
> 
> The legend is displayed by default when there is more than one category, or more than one slice with `donut`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :legend="false" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Tooltip

Use the `tooltip` prop to customize or hide the [Tooltip](https://vinicunca.dev/pohon/components/tooltip) displayed on the focused values. Defaults to `true`.

> [!TIP]
> See: #with-tooltip-slot
> 
> You can also use the `#tooltip` slot to replace its content.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :tooltip="false" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Thickness

Use the `thickness` prop to set the width of the ring in pixels. Defaults to `24`.

> [!WARNING]
> 
> This prop only works when `type` is `donut`.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart type="donut" :thickness="12" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

### Size

Use the `height` prop to set the height of the chart in pixels. Defaults to `300`.

> [!TIP]
> 
> The chart follows the width of its container. Use the `aspect-ratio` prop to derive the height from the width, or the `width` prop to set a fixed width.

> [!NOTE]
> 
> With a fixed height, the chart fades in once its container is measured. With `aspect-ratio`, the chart rendered on the server is displayed right away.

```vue
<script setup lang="ts">
import type { ChartData } from 'pohon-ui'

const data = ref<ChartData[]>([
  {
    month: "January",
    desktop: 186,
    mobile: 80
  },
  {
    month: "February",
    desktop: 305,
    mobile: 200
  },
  {
    month: "March",
    desktop: 237,
    mobile: 120
  },
  {
    month: "April",
    desktop: 73,
    mobile: 190
  },
  {
    month: "May",
    desktop: 209,
    mobile: 130
  },
  {
    month: "June",
    desktop: 214,
    mobile: 140
  }
])
</script>

<template>
  <PChart :height="200" :data="data" index="month" :categories="[
  {
    key: 'desktop',
    label: 'Desktop'
  },
  {
    key: 'mobile',
    label: 'Mobile'
  }
]" />
</template>
```

## Examples

### With formatted values

Use the `format` prop to format the values on the y axis and in the tooltip.

```vue [ChartFormatExample.vue]
<script setup lang="ts">
const data = [
  { month: 'January', revenue: 18600 },
  { month: 'February', revenue: 30500 },
  { month: 'March', revenue: 23700 },
  { month: 'April', revenue: 27300 },
  { month: 'May', revenue: 20900 },
  { month: 'June', revenue: 31400 },
];

const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact' });
</script>

<template>
  <PChart
    type="bar"
    :data="data"
    index="month"
    :categories="[{ key: 'revenue', label: 'Revenue' }]"
    :format="(value) => currency.format(value)"
    aria-label="Monthly revenue"
  />
</template>
```

### With a chart definition

Use the `definition` prop to pass a complete [TanStack Charts definition](https://tanstack.com/charts/latest/docs/concepts/chart-definitions) when the other props are not enough.

> [!NOTE]
> See: https://tanstack.com/charts/latest
> 
> A definition from the TanStack Charts examples renders as is, with the tooltip and the responsive sizing applied.

```vue [ChartDefinitionExample.vue]
<script setup lang="ts">
import { defineChart, lineY, ruleY } from '@tanstack/charts';
import { scaleLinear } from '@tanstack/charts/scales/linear';
import { scalePoint } from '@tanstack/charts/scales/point';

const data = [
  { month: 'January', revenue: 186 },
  { month: 'February', revenue: 305 },
  { month: 'March', revenue: 237 },
  { month: 'April', revenue: 273 },
  { month: 'May', revenue: 209 },
  { month: 'June', revenue: 314 },
];

const definition = defineChart({
  marks: [
    ruleY([250], { stroke: 'var(--ui-color-error)', strokeDasharray: '4 4' }),
    lineY(data, { x: 'month', y: 'revenue', stroke: 'var(--ui-color-primary)', strokeWidth: 2, points: true }),
  ],
  scales: {
    x: { scale: () => scalePoint<string>().padding(0.2) },
    y: { scale: scaleLinear, nice: true, grid: true, axis: { label: 'Revenue' } },
  },
});
</script>

<template>
  <PChart
    :definition="definition"
    aria-label="Revenue against target"
  />
</template>
```

> [!TIP]
> See: /pohon/getting-started/theme/css-variables#chart
> 
> Series without an explicit color use the `--ui-chart-*` CSS variables. Colors set in the definition are kept.

> [!CAUTION]
> 
> When the definition brings its own `tooltip`, it is displayed in place of the `tooltip` prop.

> [!WARNING]
> 
> Examples importing `d3-*` packages need them installed in your project.

### With center slot

Use the `#center` slot to display content in the middle of a `donut` chart.

```vue [ChartCenterSlotExample.vue]
<script setup lang="ts">
const data = [
  { browser: 'Chrome', visitors: 275 },
  { browser: 'Safari', visitors: 200 },
  { browser: 'Firefox', visitors: 187 },
  { browser: 'Edge', visitors: 173 },
];
</script>

<template>
  <PChart
    type="donut"
    :data="data"
    index="browser"
    :categories="['visitors']"
    legend
    aria-label="Visitors by browser"
  >
    <template #center>
      <span class="text-2xl color-text-highlighted font-semibold">835</span>
      <span class="text-xs color-text-muted">Visitors</span>
    </template>
  </PChart>
</template>
```

### With tooltip slot

Use the `#tooltip` slot to replace the tooltip content. It receives the `title`, the `rows` with their `label`, `value` and `color`, and the focused `points`.

```vue [ChartTooltipSlotExample.vue]
<script setup lang="ts">
const data = [
  { month: 'January', revenue: 18600, expenses: 12400 },
  { month: 'February', revenue: 30500, expenses: 21800 },
  { month: 'March', revenue: 23700, expenses: 25100 },
  { month: 'April', revenue: 27300, expenses: 19600 },
  { month: 'May', revenue: 20900, expenses: 22300 },
  { month: 'June', revenue: 31400, expenses: 20700 },
];

const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });

function profit(month?: string) {
  const row = data.find((row) => row.month === month);

  return row ? row.revenue - row.expenses : 0;
}
</script>

<template>
  <PChart
    type="bar"
    :data="data"
    index="month"
    :categories="[{ key: 'revenue', label: 'Revenue', color: 'success' }, { key: 'expenses', label: 'Expenses', color: 'error' }]"
    :format="(value) => currency.format(value)"
    aria-label="Revenue and expenses"
  >
    <template #tooltip="{ title, rows }">
      <p class="color-text-highlighted font-medium">
        {{ title }}
      </p>

      <div
        v-for="row in rows"
        :key="row.label"
        class="flex gap-2 items-center"
      >
        <span
          class="rounded-full size-2"
          :style="{ backgroundColor: row.color }"
        />
        <span class="color-text-muted">{{ row.label }}</span>
        <span class="ms-auto ps-4 tabular-nums">{{ row.value }}</span>
      </div>

      <PSeparator class="my-1" />

      <div class="flex gap-2 items-center">
        <span class="color-text-muted">Profit</span>
        <PBadge
          :color="profit(title) >= 0 ? 'success' : 'error'"
          :label="currency.format(profit(title))"
          variant="subtle"
          size="sm"
          class="ms-auto tabular-nums"
        />
      </div>
    </template>
  </PChart>
</template>
```

## API

### Props

```ts
/**
 * Props for the Chart component
 */
interface ChartProps {
}
```

### Slots

```ts
/**
 * Slots for the Chart component
 */
interface ChartSlots {
}
```

### Emits

No events available for this component.

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    chart: {
      slots: {
        root: [
          'relative w-full text-xs color-text-muted [&_[data-akar-popper-content-wrapper]]:pointer-events-none',
          '[--ts-chart-tooltip-background:var(--ui-color-bg)] [--ts-chart-tooltip-color:var(--ui-color-text-highlighted)] [--ts-chart-tooltip-border:1px_solid_var(--ui-color-border)] [--ts-chart-tooltip-border-radius:var(--radius-sm)] [--ts-chart-tooltip-shadow:var(--shadow-sm)] [--ts-chart-tooltip-padding:calc(var(--spacing)*1.5)_calc(var(--spacing)*2.5)] [--ts-chart-tooltip-font:inherit]'
        ],
        base: 'opacity-0 in-data-[measured]:opacity-100 transition-opacity duration-150 [&_.ts-chart]:rounded-md [&_.ts-chart]:outline-0 [&_.ts-chart]:outline-primary/25 [&_.ts-chart:focus-visible]:outline-3 [&_.ts-chart:focus-visible]:ring [&_.ts-chart:focus-visible]:ring-primary',
        tooltip: 'flex-col items-stretch gap-1 h-auto min-w-32 py-1.5 pointer-events-none',
        tooltipTitle: 'font-medium color-text-highlighted',
        tooltipRow: 'flex items-center gap-1.5 color-text-muted data-[active=true]:color-text-highlighted',
        tooltipIndicator: 'size-2 shrink-0 rounded-full',
        tooltipLabel: 'truncate',
        tooltipValue: 'ms-auto ps-4 font-medium color-text-highlighted tabular-nums',
        center: 'absolute inset-0 flex flex-col items-center justify-center pointer-events-none opacity-0 in-data-[measured]:opacity-100 transition-opacity duration-150'
      },
      variants: {
        type: {
          line: '',
          area: '',
          bar: '',
          donut: ''
        }
      }
    }
  }
})
```

## Changelog

See commit history for [component](https://github.com/vinicunca/pohon/commits/main/src/runtime/components/Chart.vue) and [theme](https://github.com/vinicunca/pohon/commits/main/src/theme/chart.ts).

---

- [TanStack Charts](https://tanstack.com/charts/latest)
- [GitHub](https://github.com/vinicunca/pohon/blob/main/src/runtime/components/Chart.vue)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
