Theme studio

Design a theme, preview it live, then export it. Saved in this browser.

Quick picks #10b981
Generated scale
50100200300400500600700800900
Some text is below AA

Bulk actions

Let people select many rows and act on them at once, with a clear count and an easy way to clear the selection.

  • Sara Khan
  • Ayesha Malik
  • Ahmed Raza
  • Omar Siddiqui

The problem

Approving thirty leave requests one by one is thirty times the work. But selection that's invisible, or actions that apply to “all” when people meant “these five”, causes expensive mistakes.

When to use it

Use it for

  • Tables and lists where people regularly handle many items the same way.
  • Actions that make sense for more than one item: approve, assign, export, delete.

Not for

  • Lists where items are only ever handled one at a time.
  • Mobile lists with swipe actions: use long-press to enter selection mode instead of always-visible checkboxes.

Anatomy

  1. Row checkboxOne per row, labelled with the row's name.
  2. Header checkboxSelects the visible page. Indeterminate when some rows are selected.
  3. Bulk barReplaces the toolbar while anything is selected: count, actions and Clear.
  4. Select allOffers to extend the selection past the current page: “Select all 248”.

How to build it

  1. Add a Checkbox to every row with aria-label set to the row's name.
  2. Add a header checkbox that selects the page and is indeterminate for partial selections.
  3. Show BulkActionBar while the selection isn't empty, with the count as its selection text.
  4. Put the two or three most common actions in the bar; the rest in a menu.
  5. For destructive bulk actions, confirm with a Dialog that repeats the count.
  6. After the action, clear the selection and confirm with a Toast.
app.tsx
import { BulkActionBar, Button, Checkbox } from "@nexera-ui/react";

{selected.size > 0 ? (
  <BulkActionBar
    selection={`${selected.size} selected`}
    onClearSelection={() => setSelected(new Set())}
  >
    <Button size="sm" onPress={approveSelected}>Approve</Button>
    <Button size="sm" variant="secondary" onPress={exportSelected}>Export</Button>
  </BulkActionBar>
) : null}

{rows.map((r) => (
  <Checkbox
    key={r.id}
    aria-label={r.name}
    isSelected={selected.has(r.id)}
    onChange={(on) => toggle(r.id, on)}
  />
))}

Accessibility

Keyboard support
KeyAction
SpaceSelects or clears the focused row's checkbox.
TabMoves from the bulk bar to the rows and back.
EscapeClears the selection when focus is in the bar.
  • Every checkbox has an accessible name: the row's name, not “Select”.
  • The selection count is announced politely when it changes.
  • The bulk bar is a labelled toolbar, so it's easy to find with a screen reader.

Do and don't

DoShow exactly how many items an action will touch.

Don'tLet “Select all” silently include rows on other pages.

DoKeep the selection when sorting or paging.

Don'tClear the selection whenever the list re-renders.

DoRepeat the count in destructive confirmations: “Delete 12 requests?”.

Don'tAsk “Delete selected items?”.