# Bar chart

> Compares categories or periods as columns or horizontal bars, grouped, stacked or 100%, with negatives below a clear zero line.

- Type: React component (Charts & Stats)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add bar-chart`
- Docs: https://wingo-ui.com/components/bar-chart

## Interaction

The bars grow out of the zero line the first time the chart scrolls into view, one category a beat after the other; value labels fade in after their bar. Move the mouse over the plot and the whole column (or row, when horizontal) under it lights up with a soft band while a readout lists every visible series in it (stacked charts add a total, 100% charts add each share). On a phone, tap a column to pin the readout above the plot, drag sideways to scrub (a vertical drag still scrolls the page), tap it again or anywhere outside to clear it. With the keyboard, Tab to the plot: Left / Right walk the categories (Up / Down for horizontal bars), Home / End jump to the ends, PageUp / PageDown move by ten, Enter fires onDatumClick for the first visible series and Escape clears; a live region reads each category. Click or tap a legend entry to hide its series (the rest regroup and the axis rescales with a glide), hover or focus one to highlight it. Switching between grouped, stacked and 100% glides every bar to its new place.

## Installation

```bash
npx wingo-ui@latest add bar-chart
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/bar-chart` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/bar-chart.tsx`

npm dependencies: `npm i motion`

Wingo UI dependencies (installed automatically):

- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))

## Usage

```tsx
import { BarChart } from "@/components/ui/bar-chart"

const data = [
  { month: "2026-07", collected: 106300, expenses: 72400 },
  { month: "2026-08", collected: 97500, expenses: 69900 },
  { month: "2026-09", collected: 128450, expenses: 81200 },
]

export function MonthlyCollections() {
  return (
    <BarChart
      data={data}
      xKey="month"
      series={[
        { key: "collected", label: "Collected" },
        { key: "expenses", label: "Expenses" },
      ]}
      valueFormat="currency"
      currency="USD"
      label="Collected vs. expenses per month"
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `data` | `ChartDatum[]` |  | One row per category, in drawing order: { month: "2026-09", collected: 128450, expenses: 81200 }. Negative values grow down (or left) from zero. |
| `xKey?` | `string` | `"date"` | The row key for the categories. ISO days ("2026-09-24") and months ("2026-09") print as local dates; other strings print as they are. |
| `series?` | `BarChartSeries[]` |  | Which keys to draw, in color and stack order: { key, label?, color?, name? }. Inferred from the numeric keys of the first rows when omitted. |
| `colors?` | `string[]` |  | Per-position palette overrides: a slot ("series-3"), a tone ("neutral", "success") or any CSS color. |
| `height?` | `number` | `240` | Plot height in px, reserved before the chart measures so nothing jumps. |
| `orientation?` | `"vertical" \| "horizontal"` | `"vertical"` | horizontal puts the categories in a label column on the left (at most 40% of the width, long names end in an ellipsis; the tooltip and table keep the full text). The better choice on phones with long names. |
| `type?` | `"grouped" \| "stacked" \| "percent"` | `"grouped"` | grouped sets the series side by side, stacked piles them (negatives below zero), percent piles their shares to 100%. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner radius of the data end: 0 / 2 / 4 / 6px or half the thickness. The base stays square; stacks round only their outermost segment. |
| `barSize?` | `number` | `24` | Maximum bar thickness in px. Bars shrink to fit narrow plots instead of overlapping. |
| `barGap?` | `number` | `2` | Px between the bars of one group and between stacked segments, painted in the surface color. |
| `categoryGap?` | `number` | `0.3` | The share of each category's band left empty between groups. |
| `showValues?` | `boolean` | `false` | A value at each bar's tip (grouped), the total at each stack's end (stacked) or the share inside each segment (percent). Every label uses one form: full amounts when they all fit, else the compact form; when they still collide every k-th category is labelled, counted back from the newest. A label with no room goes inside its bar or is skipped, never clipped; bars thinner than 14px carry none. |
| `getBarColor?` | `(datum: ChartDatum, series: ResolvedChartSeries, index: number) => (string \| undefined)` |  | A color per bar (a slot, a tone or any CSS color): "danger" for negatives, "brand" for the current month. undefined keeps the series color. |
| `highlightLast?` | `boolean` | `false` | Dims every category but the last to 45%, for the current-period look. The inspected category lights up too. |
| `valueFormat?` | `ChartValueFormat = "number" \| "compact" \| "currency" \| "percent"` | `"number"` | How values print in the tooltip, ticks, value labels and table. Percent takes whole numbers (12.4 -> "12,4 %"). |
| `currency?` | `string` | `"USD"` | ISO 4217 code for valueFormat currency, printed with the narrow symbol ("$128,450"). |
| `decimals?` | `number` |  | Fraction digits in the tooltip, labels and table; auto by magnitude when unset. Ticks stay short. |
| `formatValue?` | `(value: number, context: { series: string; kind: "tooltip" \| "tick" \| "label" \| "table" }) => string` |  | Replaces the built-in value formatting everywhere. |
| `formatX?` | `(value: unknown, context: "axis" \| "tooltip" \| "table") => string` |  | Replaces the category label formatting (short on the axis, long in the tooltip and table). |
| `locale?` | `string` | `""` | Locale for numbers and dates. Empty uses the UIProvider / LocaleProvider locale. |
| `yDomain?` | `BarChartDomain = [number \| "auto", number \| "auto"]` | `"[\"auto\", \"auto\"]"` | Fixed value bounds; zero is always included. Ignored by percent (always 0 to 100%). |
| `showGrid?` | `boolean` | `true` | Hairlines at the value ticks (horizontal lines, or vertical ones for horizontal bars). |
| `showXAxis?` | `boolean` | `true` | The labels under the plot: categories (thinned so they never collide), or value ticks for horizontal bars. |
| `showYAxis?` | `boolean` | `true` | The labels beside the plot: compact value ticks ("$128K"), or the category column for horizontal bars. |
| `yAxisSide?` | `"start" \| "end"` | `"start"` | end puts the value ticks on the right, finance style (vertical bars; horizontal bars keep categories on the left). |
| `showLegend?` | `boolean` | `true` | The legend; it only renders with two or more series (one series is named by the card title). |
| `legendPosition?` | `"top" \| "bottom"` | `"top"` | Legend above or below the plot. |
| `legendToggle?` | `boolean` | `true` | Legend entries are buttons that hide and show their series. The last visible series cannot be hidden. |
| `showTooltip?` | `boolean` | `true` | The floating readout. When off, the band, keyboard and screen reader announcements still work. |
| `tooltipMode?` | `"auto" \| "float" \| "fixed"` | `"auto"` | float rides beside the inspected bars, fixed pins above the plot (never under a finger); auto is fixed on touch screens. |
| `referenceLines?` | `{ value: number; label?: string; color?: string }[]` |  | Dashed target or average lines with a label ("Target $120,000"); always inside the value range. |
| `animated?` | `boolean` | `true` | Bars grow on first view and glide on data, type and legend changes. Reduced motion always shows the final state. |
| `loading?` | `boolean` | `false` | A bar-shaped skeleton in the plot's box. |
| `emptyState?` | `ReactNode` |  | Replaces the built-in empty state (shown when data is empty or every value is null). |
| `label?` | `string` | `""` | Accessible name of the figure and caption of its data table. Empty joins the series names. |
| `description?` | `string` | `""` | A one-sentence takeaway for screen readers ("September brought the highest collections of the year"). |
| `surface?` | `string` | `""` | The color behind the chart, used for label halos. Empty uses the surface token; set it when the chart sits on a card. |
| `hiddenSeries?` | `string[]` |  | Hidden series keys when you control legend toggling; pair with onHiddenSeriesChange. |
| `defaultHiddenSeries?` | `string[]` |  | Series keys hidden at first, when the legend toggles on its own. |
| `onHiddenSeriesChange?` | `(keys: string[]) => void` |  | Called when a legend entry hides or shows a series. |
| `activeIndex?` | `number \| null` |  | The inspected category index when you control it, so two charts can share one highlight. |
| `defaultActiveIndex?` | `number \| null` |  | The category inspected at first (a pinned readout), uncontrolled. |
| `onActiveIndexChange?` | `(index: number \| null) => void` |  | Called as the highlighted category changes (mouse, touch or keyboard) and with null when it clears. |
| `onDatumClick?` | `(event: { index: number; datum: ChartDatum; series?: string }) => void` |  | A click or tap on a category (series is the bar under the pointer, else the first visible series) or Enter on the inspected one. |
| `labels?` | `Partial<BarChartLabels>` |  | Strings: empty, loading, table, total, inspectHint, inspectHintVertical, showSeries, hideSeries, category (Category, the table's first column). |
| `classNames?` | `Partial<Record<BarChartSlot, string>>` |  | Classes for legend, plot, tooltip, table, grid, xAxis, yAxis, bar, band, value, category, referenceLine and inspect. |

## Parts

### DEFAULT_BAR_CHART_LABELS (constant)

The English strings the chart uses (empty and loading text, the data table and its Category column, the keyboard hints for both orientations, show and hide series). Spread it and override what you translate.

### BarChartLabels (type)

The shape of labels: the chart kit's shared strings plus category and inspectHintVertical.

### BarChartSeries (type)

One bar key: { key, label?, color?, name? }, in color and stack order.

## Examples

- **Negative values**: Cash-flow variation: bars grow down from zero, getBarColor paints them by sign.
- **Horizontal, long names**: Categories in a label column with compact value labels at the tips.
- **Stacked with totals**: Invoices by status per month, the total above each stack.
- **Current period**: highlightLast on 12 weeks of orders in the neutral ink.
- **100% stacked**: Channel shares per quarter with the share inside each segment.
- **Loading and empty**: The skeleton and the empty state in the plot's box.
- **Phone, 360px**: Horizontal bars with the pinned touch readout.

## See also

- [Chart kit](https://wingo-ui.com/components/chart-kit) ([Markdown](https://wingo-ui.com/components/chart-kit.md))
- [Line chart](https://wingo-ui.com/components/line-chart) ([Markdown](https://wingo-ui.com/components/line-chart.md))
- [Area chart](https://wingo-ui.com/components/area-chart) ([Markdown](https://wingo-ui.com/components/area-chart.md))
- [Funnel chart](https://wingo-ui.com/components/funnel-chart) ([Markdown](https://wingo-ui.com/components/funnel-chart.md))
- [Donut chart](https://wingo-ui.com/components/donut-chart) ([Markdown](https://wingo-ui.com/components/donut-chart.md))

---

Canonical: https://wingo-ui.com/components/bar-chart
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
