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

Components · Charts

# BarList

A ranked list with an inline bar per row: top pages, referrers, rivals by score.

@rookcom/monarch-react/charts

Component

## Top pages with links

## Usage examples

### Ranked comparison, inline layout

### Rates with a description and display value

### Loading and empty

## Purpose

Compares a handful of named values at a glance. Each row keeps its label and value as text, so the list reads correctly without the bars.

### Use when

- Top-N breakdowns: pages, referrers, campaigns, countries, engines.
- Comparing your entity against others (`highlighted`), with missing values shown honestly.

### Avoid when

- Values over time: use a line or bar chart.
- Parts of one whole that should add to 100%: use StackedProportionBar or a donut.
- More than about 10 rows or several columns of numbers: use a DataTable.

## Design principles

- Text first: labels and values are real list text; bars are decorative (`aria-hidden`).
- Largest first by default; missing values sort last and read as "No value", never as zero.
- One series colour (the first data palette colour); a highlighted row keeps it and the others fade.
- Values are formatted with Intl.NumberFormat (or Money for currency) in the viewer’s locale.

## API reference

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

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `BarList` | component | server-safe | `.mn-bar-list` |

The ref and native attributes go to the root `<div>`; the list is a `<ul>`, or an `<ol>` when `ranked`.

Exported from `@rookcom/monarch-react/charts`, with the `BarListItem` type.

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

## Properties

Bars are scaled to the largest value unless `max` is set. For ratios of a whole, pass `max={1}`.

### `BarList`

A ranked list with an inline bar per row (top pages, top referrers, rivals by score). Labels and values are real text; the bars are decorative.

| Prop | Type | Description |
| --- | --- | --- |
| `items` required | `BarListItem[]` |  |
| `className` | `string` |  |
| `currency` | `string` | ISO 4217 code for `format="currency"`. |
| `emptyState` | `ReactNode` | Shown when there are no items. Default "No data yet". |
| `format` | `"number" \| "currency" \| "percent"` | How values are written: `number` (default), `percent` (a ratio: 0.12 is 12%) or `currency`. |
| `formatOptions` | `Intl.NumberFormatOptions` | Extra `Intl.NumberFormat` options. |
| `formatValue` | `((value: number, item: BarListItem) => ReactNode)` | Writes a value yourself; replaces `format`. |
| `id` | `string` |  |
| `label` | `string` | Names the list for assistive technology, e.g. "Top pages". |
| `layout` | `"inline" \| "stacked"` | `stacked` (default): bar under the label. `inline`: label, bar and value in one row on wide screens. |
| `limit` | `number` | Shows only the first `limit` rows, with a "Top 5 of 12" footer. |
| `linkComponent` | `ElementType` | Component used for `href` rows, e.g. a router link. Default `a`. |
| `loading` | `boolean` | Shows placeholder rows and sets `aria-busy`. |
| `loadingRows` | `number` | Placeholder rows while loading. Default 5. |
| `locale` | `string` | BCP 47 locale. Default: the runtime locale. |
| `max` | `number` | The value of a full bar. Default: the largest value. Pass `1` for ratios of a whole. |
| `missingLabel` | `string` | Spoken for a missing value. Default "No value". |
| `notation` | `"compact" \| "standard"` | `compact` writes 1.2K. Default `standard`. |
| `overflowLabel` | `ReactNode` | Replaces the default "Top {shown} of {total}" footer when rows are cut by `limit`. |
| `ranked` | `boolean` | Numbers the rows and renders an ordered list. Default `false`. |
| `sort` | `"desc" \| "none" \| "asc"` | Row order. `desc` (default) puts the largest first; `none` keeps the given order. Missing values go last. |
| `unit` | `string` | Unit after a `number` value, e.g. `visits`. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `layout` | `'stacked'``'inline'` | `stacked` (default) puts a thin bar under each label; `inline` puts label, bar and value on one row from 30rem up (`data-layout`). |
| `format` | `'number'``'percent'``'currency'` | How values are written; `notation="compact"`, `unit` and `formatOptions` refine it, `formatValue` replaces it. |
| `sort` | `'desc'``'asc'``'none'` | Row order; `none` keeps the given order. |
| `ranked` | `'false'``'true'` | Shows rank numbers and renders an ordered list (`data-ranked`). |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Highlighted row | `data-highlighted` | Bold label; the other rows’ bars fade. Put "you" in the label too. |
| Missing value | `data-missing` | A dash read as `missingLabel` ("No value"), no bar, sorted last. |
| Linked row | `data-linked` | The label is a link and the whole row is its click target. |
| Overflow | – | With `limit`, a "Top 5 of 12" footer (or `overflowLabel`). |
| Loading | `data-loading` | Placeholder rows, `aria-busy`, and a hidden "Loading {label}". |
| Empty | `data-empty` | `emptyState` text instead of a list. |

## Interaction behaviour

- Rows with `href` are links; clicking anywhere on the row follows the link.
- Hovering a linked row tints it and underlines the label.

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab / Shift+Tab | Moves between linked rows. |
| Enter | Follows the focused row’s link. |

Rows without `href` are not focusable.

## Accessibility

Role

list (

<ul>

or

<ol>

, with an explicit role so Safari keeps it)

Accessible name

label

, e.g. "Top pages".

- Each row reads its label, description and value; the bar is hidden from assistive technology.
- A missing value is announced as "No value", not only shown in a tooltip.
- Rank numbers are hidden because an ordered list already announces positions.
- Linked rows have a focus ring around the whole row and are named by their label.
- In forced-colours mode bars are drawn in CanvasText with an outlined track.

## Responsive behaviour

- Labels and descriptions truncate with an ellipsis; values never wrap.
- The inline layout falls back to stacked below 30rem, so it works at 320px.

## Theme support

- Bars use `--mn-color-data-1` on a `--mn-color-bg-muted` track, which are defined for light, dark and high-contrast themes.
- Values use the default text colour; rank and descriptions are muted.

## Do and don't

### Do

- Name the list with `label` and keep labels short; use `description` for the second detail.
- Pass `null` for an unknown value so it reads as "No value" instead of a misleading zero.
- Use `limit` with an honest "Top 5 of 12" footer, and link to the full list.

### Don't

- Do not put information only in the bar length; the value text is what everyone reads.
- Do not colour rows with status colours to rank them.
- Do not use a BarList for shares that must add to 100%.

## Related components

- [BarChart](https://monarchui.dev/components/bar-chart/): Values over time or many categories with axes.
- [StackedProportionBar](https://monarchui.dev/components/stacked-proportion-bar/): Parts of one whole in a single bar.
- [DataTable](https://monarchui.dev/components/data-table/): Many rows or several numeric columns.
- [Progress](https://monarchui.dev/components/progress-bar/): One value against a target or quota.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Money](https://monarchui.dev/components/money/), [Skeleton](https://monarchui.dev/components/skeleton/), [VisuallyHidden](https://monarchui.dev/components/visually-hidden/) |

## Testing coverage

- Named list, largest first, text values and decorative bars scaled to the largest value.
- sort none/asc, ranked ordered list.
- Percent, currency, compact, unit and custom formatting.
- Missing values announced and sorted last; display values.
- Linked rows reachable with Tab, custom router link component.
- limit with default and custom overflow footer; highlighted rows.
- Empty and loading states; zero values; ref and layout.

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

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

## All example code

### Top pages with links

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

const pages = [
  { id: 'home', label: '/', value: 4820, href: '#/components/bar-list' },
  { id: 'pricing', label: '/pricing', value: 2310, href: '#/components/bar-list' },
  { id: 'docs', label: '/docs/getting-started', value: 1640, href: '#/components/bar-list' },
  { id: 'blog', label: '/blog/release-notes', value: 920, href: '#/components/bar-list' },
  { id: 'contact', label: '/contact', value: 310, href: '#/components/bar-list' },
];

<div style={{ width: '100%', maxWidth: '28rem' }}>
  <BarList label="Top pages" items={pages} unit="views" locale="en-US" />
</div>
```

### Ranked comparison, inline layout

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

<div style={{ width: '100%', maxWidth: '36rem' }}>
  <BarList
    label="Visibility score"
    ranked
    layout="inline"
    max={100}
    limit={4}
    items={[
      { id: 'a', label: 'Northfield', value: 82 },
      { id: 'you', label: 'Your site (you)', value: 71, highlighted: true },
      { id: 'b', label: 'Cobalt', value: 64 },
      { id: 'c', label: 'Fernway', value: null },
      { id: 'd', label: 'Tidewater', value: 38 },
    ]}
  />
</div>
```

### Rates with a description and display value

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

<div style={{ width: '100%', maxWidth: '28rem' }}>
  <BarList
    label="Reply rate by channel"
    format="percent"
    max={1}
    items={[
      {
        id: 'e',
        label: 'Email',
        description: 'Spring outreach',
        value: 0.12,
        displayValue: '12% · 4 of 33',
      },
      {
        id: 'l',
        label: 'LinkedIn',
        description: 'Founders list',
        value: 0.21,
        displayValue: '21% · 9 of 43',
      },
      { id: 'c', label: 'Calls', description: 'Renewals', value: 0.05 },
    ]}
    locale="en-US"
  />
</div>
```

### Loading and empty

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

<div style={{ display: 'grid', gap: '1.5rem', width: '100%', maxWidth: '28rem' }}>
  <BarList label="Top referrers" items={[]} loading loadingRows={3} />
  <BarList label="Top referrers" items={[]} emptyState="No referrers in this period" />
</div>
```
