Source: https://monarchui.dev/components/bar-chart/

Components · Charts

# BarChart

Vertical and horizontal bar charts, grouped or stacked, with value labels, highlight, reference lines, keyboard tooltips, drill-in and a data table.

@rookcom/monarch-react/charts

Component

## Grouped columns

## Usage examples

### Stacked with totals

### Highlighted month with a target

### Horizontal, with drill-in

## Purpose

Compares amounts across categories or discrete periods, one or more series per category, with exact values on hover, tap or arrow keys and the full data as a table.

### Use when

- Comparing categories: requests per topic, revenue per month, posts per week.
- Showing parts of a total per category (`layout="stacked"`).
- Long category names or rankings (`orientation="horizontal"`).

### Avoid when

- A continuous trend with many points: use LineChart.
- A simple ranked list with values and links: use BarList.
- Shares of one whole: use StackedProportionBar or DonutChart.

## Design principles

- Bars always start at zero; the value axis can only fix its far end.
- Only the far end of a bar is rounded (4px), stacked segments are separated by a surface gap, and only the outer segment is rounded.
- Colour encodes the series, never the category; one series is one colour, and `highlightIndex` emphasises by dimming the rest.
- Value labels are optional and compact (1.2K); exact values live in the tooltip and table.

## API reference

```tsx
import { BarChart } from '@rookcom/monarch-react/charts';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `BarChart` | component | client | `.mn-chart` |

Renders ChartFrame (ref to its `<figure>`); ChartFrame props pass through except `children`, `legend` and `table`, which BarChart builds.

`data` is one object per category; `categoryKey` names the category field and each series `key` a numeric field.

`height` defaults to 240 for vertical charts; horizontal charts grow by 32px per category.

Generated from the TypeScript declarations of `packages/react/src/components/charts/bar-chart`.

## Properties

### `BarChart`

Bar and column chart: one or more series per category, grouped or stacked, vertical or horizontal, with value labels, a highlighted category, reference lines, tooltips on hover, touch and keyboard, legend toggles, drill-in and an accessible data table.

| Prop | Type | Description |
| --- | --- | --- |
| `categoryKey` required | `string` | Field holding the category: a label, a number or a `Date` (formatted with `categoryFormat`). |
| `data` required | `readonly ChartDatum[]` | One row per category, in display order, with one number per series. Missing values draw no bar. |
| `series` required | `readonly ChartSeries[]` | The measures to draw. Colours follow the order unless pinned. |
| `title` required | `ReactNode` | The chart's name. Labels the figure. Required. |
| `actions` | `ReactNode` | Period or metric controls in the header, such as a pill TabsFilter or a Select. |
| `activeIndex` | `number \| null` | Controlled inspected category (`null` for none). |
| `categoryFormat` | `Intl.DateTimeFormatOptions` | `Intl.DateTimeFormat` options when categories are dates. Default `{ month: 'short', day: 'numeric' }`. |
| `categoryLabel` | `string` | Column header for the categories in the data table. Default: `categoryKey`. |
| `className` | `string` |  |
| `defaultHiddenSeries` | `readonly string[]` | Uncontrolled initial hidden series. |
| `defaultShowTable` | `boolean` | Uncontrolled initial table view. Default `false`. |
| `description` | `ReactNode` | One line under the title: the period, unit or what the chart answers. Describes the figure. |
| `emptyActions` | `ReactNode` | Actions in the empty state, such as a link to set up tracking. |
| `emptyDescription` | `ReactNode` | Empty-state description, such as what will appear and when. |
| `footer` | `ReactNode` | Notes under the chart: source, last updated, a footnote. |
| `formatCategory` | `((value: XValue, index: number) => string)` | Formats categories everywhere. Wins over `categoryFormat`. |
| `formatTick` | `ValueFormatter` | Formats value-axis ticks. Default: compact notation of `valueFormat`. |
| `formatValue` | `ValueFormatter` | Formats values in the tooltip, table, labels and summary. Wins over `valueFormat`. |
| `headingLevel` | `ChartFrameHeadingLevel` | Render the title as a heading (`h2`–`h6`) to match the page outline. Default: not a heading. |
| `height` | `number` | Height of the chart in px. Default 240 vertical; horizontal grows with the number of categories (32px each). |
| `hiddenSeries` | `readonly string[]` | Controlled list of hidden series keys. |
| `highlightIndex` | `number` | Emphasise one category (the current month, say); the others are dimmed. |
| `id` | `string` |  |
| `labels` | `BarChartLabels` |  |
| `layout` | `BarChartLayout` | With several series: side by side (`grouped`, default) or one stacked bar per category. Exposed as `data-layout`. |
| `legendToggle` | `boolean` | Legend entries toggle their series. Default `true` (legend shows for two or more series). |
| `locale` | `string` | BCP 47 locale for numbers and dates. Pass it when rendering on the server. |
| `onActiveIndexChange` | `((index: number \| null) => void)` |  |
| `onBarSelect` | `((datum: ChartDatum, index: number) => void)` | Click, Enter or Space on the inspected category, for drill-in (open the filtered list). |
| `onHiddenSeriesChange` | `((hidden: string[]) => void)` |  |
| `onRetry` | `(() => unknown)` | Error-state retry. Return the refetch promise to keep the button busy until it settles. |
| `onShowTableChange` | `((showTable: boolean) => void)` |  |
| `orientation` | `BarChartOrientation` | `vertical` (default) columns, or `horizontal` bars for long category names and rankings. Exposed as `data-orientation`. |
| `referenceLines` | `readonly ChartReferenceLine[]` | Dashed lines across the bars for targets or averages. |
| `showTable` | `boolean` | Controlled table view. |
| `status` | `ChartFrameStatus` | `ready` (default) shows the chart; `loading` a skeleton with `aria-busy`; `empty` an empty state; `error` an error with Try again when `onRetry` is set. Exposed as `data-status`. |
| `summary` | `string` | Replaces the generated text summary (the plot's accessible name). |
| `tableToggle` | `boolean` | Show the "View as table" toggle. Default `true` when `table` is set. |
| `valueDomain` | `readonly [ChartDomainBound, ChartDomainBound]` | Fix the far end of the value axis (bars always start at zero). |
| `valueFormat` | `Intl.NumberFormatOptions` | `Intl.NumberFormat` options for values in the tooltip, table, labels and summary. |
| `valueLabels` | `boolean` | Print each bar's value (the stack total when stacked) at its end. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `orientation` | `'vertical'``'horizontal'` | Columns, or bars with category labels on the left (truncated with an ellipsis). `data-orientation`. |
| `layout` | `'grouped'``'stacked'` | Series side by side, or one bar per category with positive and negative parts stacked separately and a Total column in the table. `data-layout`. |
| `valueLabels` | `'true'``'false'` | Each bar’s value (the stack total when stacked) at its end. |
| `series[].dashed` | `'true'``'false'` | An outlined bar instead of a filled one, as a second encoding (a forecast, a target). |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Inspecting | – | A muted band behind the category and a tooltip with every visible series (and the total when stacked). |
| Highlighted | `data-dimmed on the other bars` | With `highlightIndex`, every other category is dimmed. |
| Selectable | `data-selectable` | With `onBarSelect` the plot shows a pointer cursor and Enter selects. |
| Series hidden | `aria-pressed="false"` | The series leaves the plot and the axis; it stays in the table. |
| Empty / loading / error | `data-status` | From ChartFrame. |

## Interaction behaviour

- Hover or tap a category to inspect it; leaving with the mouse clears it.
- `onBarSelect(datum, index)` runs on click, Enter or Space, for drill-in such as opening a filtered list.
- Legend entries toggle series (controlled `hiddenSeries` or uncontrolled `defaultHiddenSeries`).
- `activeIndex` + `onActiveIndexChange` sync the inspected category with other charts.

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab | Focuses the plot (one tab stop) and inspects the first category. |
| ArrowRight / ArrowDown | Next category (either pair works in both orientations). |
| ArrowLeft / ArrowUp | Previous category. |
| Home / End | First or last category. |
| PageUp / PageDown | Moves a tenth of the categories. |
| Escape | Hides the tooltip. |
| Enter / Space | Calls `onBarSelect` for the inspected category. |

## Accessibility

Role

figure (ChartFrame); the plot is a group with aria-roledescription "chart"

Accessible name

The figure by

title

; the plot by a generated summary (count, then total and highest category per series) or

summary

.

- The SVG is `aria-hidden`; the summary, a polite live region during keyboard inspection and the data table carry the values.
- The tooltip is visual only, so pointer use does not flood screen readers.
- The data table has every series and, when stacked, a Total column.
- Emphasis is not colour alone: the highlighted category keeps full opacity while others dim, and value labels can show the numbers.
- All text, including summaries and "Total", is translatable through `labels`.

## Responsive behaviour

- Lays out at the measured container width (works at 320px); bar thickness is capped so few bars do not turn into slabs.
- Vertical category labels thin out when they would collide; horizontal labels truncate to at most 40% of the width.
- Server render and first paint use a 640px layout scaled to fit, matching on hydration.

## Theme support

- Series use `--mn-color-data-1` … `-5`; stacked segments are separated by a `--mn-color-bg-surface` stroke.
- Light, dark and high contrast follow the tokens; data colours meet 3:1 (4.5:1 in high contrast) on every chart background.
- Dimming uses opacity with a fast, token-timed transition that is instant under reduced motion.

## Do and don't

### Do

- Sort categories meaningfully: by time for periods, by value for rankings.
- Use horizontal bars when labels are longer than a word or two.
- Fill missing periods with zero in your data when zero is the truth; leave them out when it is not.

### Don't

- Colour bars by category or by value; one series is one colour.
- Stack more than about four series; the inner segments become unreadable.
- Start the value axis anywhere but zero (BarChart does not allow it).

## Related components

- [ChartFrame](https://monarchui.dev/components/chart-frame/): The frame BarChart renders.
- [LineChart](https://monarchui.dev/components/line-chart/): Continuous trends with many points.
- [BarList](https://monarchui.dev/components/bar-list/): A ranked list of labelled values with links, no axes.
- [StackedProportionBar](https://monarchui.dev/components/stacked-proportion-bar/): Shares of a single total in one bar.
- [Sparkline](https://monarchui.dev/components/sparkline/): A tiny sparkbar (`variant="bar"`) inside a cell or card.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [ChartFrame](https://monarchui.dev/components/chart-frame/) |

Hand-written SVG from `src/internal/charts`; no charting library.

## Testing coverage

- Grouped bars per category, legend toggles and a generated summary; no axe violations.
- Stacked: Total column and tooltip footer, only the outer segment rounded.
- Keyboard inspection with live announcements; Enter and Space call `onBarSelect`; band highlight.
- Tap and click select a category.
- Horizontal layout: category labels, value labels, auto height, no legend for one series.
- `highlightIndex` dims the other categories.
- Negative values extend below zero.
- Hiding a series keeps the other colour.
- Locale-aware percent formatting.
- Renders on the server.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/bar-chart; Cross-browser axe: /components/bar-chart |

Tests: `packages/react/src/components/charts/bar-chart`

## All example code

### Grouped columns

```tsx
import { BarChart } from '@rookcom/monarch-react/charts';

const WEEKS = [
  { week: 'Week 1', drafted: 12, posted: 8 },
  { week: 'Week 2', drafted: 9, posted: 9 },
  { week: 'Week 3', drafted: 15, posted: 11 },
  { week: 'Week 4', drafted: 11, posted: 10 },
  { week: 'Week 5', drafted: 14, posted: 13 },
  { week: 'Week 6', drafted: 18, posted: 12 },
];

<BarChart
  title="Posts"
  description="Drafted and posted per week"
  data={WEEKS}
  categoryKey="week"
  categoryLabel="Week"
  series={[
    { key: 'drafted', label: 'Drafted' },
    { key: 'posted', label: 'Posted' },
  ]}
  locale="en-US"
/>
```

### Stacked with totals

```tsx
import { BarChart } from '@rookcom/monarch-react/charts';

const WEEKS = [
  { week: 'Week 1', drafted: 12, posted: 8 },
  { week: 'Week 2', drafted: 9, posted: 9 },
  { week: 'Week 3', drafted: 15, posted: 11 },
  { week: 'Week 4', drafted: 11, posted: 10 },
  { week: 'Week 5', drafted: 14, posted: 13 },
  { week: 'Week 6', drafted: 18, posted: 12 },
];

<BarChart
  title="Posts by state"
  data={WEEKS}
  categoryKey="week"
  categoryLabel="Week"
  layout="stacked"
  valueLabels
  series={[
    { key: 'posted', label: 'Posted' },
    { key: 'drafted', label: 'Drafted' },
  ]}
  locale="en-US"
/>
```

### Highlighted month with a target

```tsx
import { BarChart } from '@rookcom/monarch-react/charts';

const MONTHS = [
  { month: 'May', earned: 320 },
  { month: 'Jun', earned: 410 },
  { month: 'Jul', earned: 380 },
  { month: 'Aug', earned: 520 },
  { month: 'Sep', earned: 610 },
  { month: 'Oct', earned: 680 },
];

<BarChart
  title="Earnings"
  description="Last six months"
  data={MONTHS}
  categoryKey="month"
  categoryLabel="Month"
  series={[{ key: 'earned', label: 'Earned' }]}
  highlightIndex={MONTHS.length - 1}
  valueLabels
  referenceLines={[{ value: 500, label: 'Goal' }]}
  valueFormat={{ style: 'currency', currency: 'USD', maximumFractionDigits: 0 }}
  locale="en-US"
  height={200}
/>
```

### Horizontal, with drill-in

```tsx
import { BarChart } from '@rookcom/monarch-react/charts';

const CATEGORIES = [
  { topic: 'Billing and invoices', open: 42 },
  { topic: 'Account access', open: 31 },
  { topic: 'Integrations', open: 18 },
  { topic: 'Feature requests', open: 12 },
  { topic: 'Other', open: 7 },
];

<BarChart
  title="Open requests by topic"
  data={CATEGORIES}
  categoryKey="topic"
  categoryLabel="Topic"
  orientation="horizontal"
  valueLabels
  series={[{ key: 'open', label: 'Open' }]}
  onBarSelect={() => undefined}
  locale="en-US"
/>
```
