Source: https://monarchui.dev/components/bulk-action-bar/

Components · Tables and data grid

# BulkActionBar

Toolbar shown while items are selected: count, actions with an overflow menu, clear selection and a polite announcement.

@rookcom/monarch-react/data

Component

## With DataTable selection

## Usage examples

### Busy

## Purpose

Gives people the actions that apply to everything they selected in a table or list, says how many items are selected, and clears the selection. It appears with the first selection and disappears when the selection is empty.

### Use when

- A DataTable or list supports multi-select and several actions apply to the selection.
- The selection count should be visible and announced.

### Avoid when

- One action per row: use DataTable `rowActions`.
- Page-level actions unrelated to a selection: use PageHeader or Toolbar.

## Design principles

- Selection-aware: hidden at 0, the count is always the first thing read.
- The app owns the selection and runs the actions; the bar renders and reports. Confirmations, previews and progress for long jobs stay in the app (ConfirmDialog, Toast).
- A promise returned by an action makes that button busy and blocks the others, so an action cannot run twice.
- One tab stop: arrow keys move between its controls (WAI-ARIA toolbar).

## API reference

```tsx
import { BulkActionBar, defaultBulkActionBarMessages } from '@rookcom/monarch-react/data';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `BulkActionBar` | component | client | `.mn-bulk-action-bar` |
| `defaultBulkActionBarMessages` | utility | client | – |

Imported from `@rookcom/monarch-react/data`. The ref is the outer `div`, which also receives focus after "Clear selection" unless `returnFocusRef` is given.

`BulkAction`: `id`, `label`, `icon`, `onSelect` (may return a promise), `tone`, `disabled`.

`messages` translates the count ("3 of 120 selected"), "Selection cleared", "Clear selection", "More" and the toolbar name.

Generated from the TypeScript declarations of `packages/react/src/components/data-grid/bulk-action-bar`.

## Properties

### `BulkActionBar`

Toolbar that appears while items are selected: the count, actions with an overflow menu, and "Clear selection". The count is announced politely. Arrow keys move between its controls (one tab stop).

| Prop | Type | Description |
| --- | --- | --- |
| `count` required | `number` | Number of selected items. The bar is shown while it is above 0. |
| `onClear` required | `() => void` | Empties the selection (DataTable: set `selectedRowIds` to `[]`). |
| `actions` | `readonly BulkAction[]` | Actions shown as buttons; those beyond `maxVisibleActions` go into a "More" menu. |
| `busy` | `boolean` | An action is running: every action is blocked. |
| `children` | `ReactNode` | Extra controls in the toolbar, e.g. a property picker. |
| `className` | `string` |  |
| `id` | `string` |  |
| `maxVisibleActions` | `number` | Buttons shown before the overflow menu. Default 3. |
| `messages` | `Partial<BulkActionBarMessages>` |  |
| `placement` | `"sticky" \| "inline"` | `sticky` keeps the bar at the bottom of the scroll area. Default `inline`. |
| `returnFocusRef` | `RefObject<HTMLElement \| null>` | Where focus goes after "Clear selection". Default: the bar's container. |
| `totalCount` | `number` | Total items that could be selected, for "3 of 120 selected". |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `placement` | `'inline'``'sticky'` | `sticky` keeps the bar at the bottom of the scroll area with a stronger shadow (`data-placement`). |
| `maxVisibleActions` | `'number'` | Actions after this many (default 3) go into the "More" menu. |
| `tone (action)` | `'default'``'danger'` | Danger actions use the subtle danger button and danger menu item. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Closed | `data-state="closed"` | Nothing selected: only the live region renders. |
| Open | `data-state="open"` | Something is selected: the toolbar is shown. |
| Busy | `aria-busy` | `busy`, or an action whose promise has not settled: that button shows a spinner and the rest are disabled. |

## Interaction behaviour

- Pressing an action calls its `onSelect`; overflow actions are in a "More" menu.
- "Clear selection" calls `onClear` and moves focus to `returnFocusRef` (or the bar container) so it is never lost.
- Count changes are announced politely ("2 of 4 selected", then "Selection cleared").
- Extra controls (property pickers) can be passed as children.

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab | Moves into the toolbar (one stop) and out again. |
| Arrow Left / Right | Moves between the toolbar’s controls, wrapping (RTL aware). |
| Home / End | First / last control. |
| Enter / Space | Runs the focused action or opens "More". |

## Accessibility

Role

toolbar

Accessible name

"Bulk actions" (

messages.label

).

- A persistent polite status region announces count changes, so the announcement works even when the bar first appears.
- The visible count is plain text inside the toolbar; icons are decorative.
- Busy actions keep focus (aria-disabled via Button `loading`).

## Responsive behaviour

- Wraps onto several lines; below 480px the actions take their own full-width row under the count and Clear.

## Theme support

- Raised surface, border and shadow tokens, so it reads in every theme and contrast mode.
- The entrance animation is removed with `prefers-reduced-motion: reduce`.

## Do and don't

### Do

- Confirm destructive bulk actions and report partial failures (Toast).
- Return a promise from long actions so the bar blocks double runs.
- Pass `totalCount` when "select all matching" is possible.

### Don't

- Do not show the bar without a way to clear the selection.
- Do not put more than a few actions in view; let the rest overflow.

## Related components

- [DataTable](https://monarchui.dev/components/data-table/): Supplies the selection.
- [Toolbar](https://monarchui.dev/components/toolbar/): Actions that do not depend on a selection.
- [ConfirmDialog](https://monarchui.dev/components/confirm-dialog/): Confirm destructive bulk actions.
- [Toast](https://monarchui.dev/components/toast/): Report results and offer undo.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [Button](https://monarchui.dev/components/button/), [DropdownMenu](https://monarchui.dev/components/dropdown-menu/) |

## Testing coverage

- Hidden until DataTable rows are selected; announces "1 of 3 selected", then the new count.
- Clear empties the selection, announces it and keeps focus (container or returnFocusRef).
- Runs actions; overflow menu; a pending promise blocks other actions and sets aria-busy.
- Roving tabindex: one tab stop, arrows wrap, Home/End.
- Translated messages and sticky placement; server rendering open and closed.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | None yet |

Tests: `packages/react/src/components/data-grid/bulk-action-bar`

## All example code

### With DataTable selection

```tsx
import { useState } from 'react';
import { BulkActionBar, DataTable, type DataTableColumn } from '@rookcom/monarch-react/data';

interface Member {
  id: string;
  name: string;
  team: string;
  role: string;
}

const MEMBERS: Member[] = [
  { id: 'm1', name: 'Ana Silva', team: 'Support', role: 'Agent' },
  { id: 'm2', name: 'Ben Okafor', team: 'Support', role: 'Lead' },
  { id: 'm3', name: 'Chen Wei', team: 'Billing', role: 'Agent' },
  { id: 'm4', name: 'Dev Patel', team: 'Sales', role: 'Manager' },
];

const COLUMNS: DataTableColumn<Member>[] = [
  { id: 'name', header: 'Name' },
  { id: 'team', header: 'Team' },
  { id: 'role', header: 'Role' },
];

function WithTable() {
  const [selected, setSelected] = useState<string[]>(['m1', 'm3']);
  const [log, setLog] = useState('');
  const act = (what: string) => () => setLog(`${what}: ${selected.length} members`);
  return (
    <div style={{ display: 'grid', gap: 8 }}>
      <BulkActionBar
        count={selected.length}
        totalCount={MEMBERS.length}
        onClear={() => setSelected([])}
        actions={[
          { id: 'team', label: 'Move to team', onSelect: act('Moved') },
          { id: 'role', label: 'Change role', onSelect: act('Role changed') },
          { id: 'export', label: 'Export', onSelect: act('Exported') },
          { id: 'remove', label: 'Remove', tone: 'danger', onSelect: act('Removed') },
        ]}
      />
      <DataTable
        columns={COLUMNS}
        rows={MEMBERS}
        getRowId={(r) => r.id}
        getRowLabel={(r) => r.name}
        caption="Members"
        selectable
        selectedRowIds={selected}
        onSelectedRowIdsChange={setSelected}
      />
      <p style={{ margin: 0, fontSize: 13 }}>{log || 'Pick an action.'}</p>
    </div>
  );
}

<WithTable />
```

### Busy

```tsx
import { BulkActionBar } from '@rookcom/monarch-react/data';

function Busy() {
  return (
    <BulkActionBar
      count={12}
      onClear={() => {}}
      busy
      actions={[
        { id: 'archive', label: 'Archive', onSelect: () => {} },
        { id: 'tag', label: 'Add tag', onSelect: () => {} },
      ]}
    />
  );
}

<Busy />
```
