Skip to content

Bulk Actions

Selecting many records and doing one thing to all of them — and designing what happens when it works for most but not all.

UI Components

Bulk actions let somebody select many records and apply one operation to all of them. In a B2B product this is usually where the real work happens: a support queue is triaged in batches, a migration reassigns hundreds of records, a clean-up archives a year of them.

The selection mechanics are straightforward. Everything difficult about this pattern lives in two places: what "all" means, and what happens when the operation partly fails.

What "select all" means

The checkbox in a data table header selects the visible page. Most users read it as selecting everything matching the current filter.

That gap causes real damage in both directions — somebody archives fifty records believing they archived nine hundred, or expects a full export and gets one page of it.

The pattern that resolves it is explicit and takes one line. Selecting the page shows a message: All 50 on this page are selected. Select all 912 matching your filter. Both options are visible, the counts are stated, and nobody has to guess which one the checkbox meant.

Partial failure is the normal case

Forty rows selected, thirty-four processed, six rejected because they are locked, already archived, or belong to a project this person cannot edit.

This is not an edge case. At any real scale it is the common outcome, and it is almost never designed.

Report the split, with numbers. Thirty-four succeeded, six did not.

Keep the failures reachable. Leave them selected, or offer a filter that isolates them. The worst version is a toast saying "6 failed" that disappears in five seconds with no route to the six.

Say why, per row. The six probably failed for two different reasons, and one of them may be fixable in a click.

Decide on atomicity, and state it. Did the thirty-four go through, or was the whole batch rolled back? Both are defensible; leaving it unstated is not, because the person's next action depends on it.

Friction proportional to damage

Reversible operations use undo, not confirmation. Archive forty, show an undo, and the person who meant it pays nothing.

Irreversible operations at scale deserve a real confirmation that names the count and the consequence: Delete 912 records permanently? This cannot be undone. A generic "Are you sure?" is dismissed reflexively by the third time.

For the genuinely destructive, make it awkward. Typing the count, or the name of the thing being emptied, is worth the annoyance in proportion to what is at stake.

Never confirm the routine. A confirmation on a frequent, reversible bulk action is a click charged forever and stops being read almost immediately.

The mechanics that get forgotten

Selection across pages. Preserve it with a running count, or state that it is page-only. Silent clearing on page change is the defect that makes people avoid paging.

A visible count and a clear-all. Somewhere persistent — a bar that appears when a selection exists, showing 12 selected and a way to drop it.

Shift-click for ranges. Expected by anybody who works with lists, and cheap.

Long operations need progress. Nine hundred records is not instant. A blocking spinner is the wrong answer; a progress indication, or a background job with a notification, is.

Permissions filter the action list. Show only what this person can do to this selection, or explain the ones they cannot.

In practice

A support queue supports bulk reassignment. An agent filters to a project, selects all, reassigns, and the interface reports success.

Two days later a supervisor finds forty unassigned tickets. The agent had selected the visible page — fifty of two hundred and thirty — and the interface had said "Reassigned" without a number. Nothing failed; the operation did exactly what was asked, and what was asked was not what was meant.

The repair is three sentences of interface. The select-all message now states both counts and offers the larger one. The result message names the number reassigned rather than saying "Reassigned". And the bar showing the selection stays visible while the selection exists, so the count is on screen at the moment the button is pressed.

Where teams get it wrong

  • Ambiguous select-all. Read as everything, meaning the page.
  • No number in the result. "Done" where a count was needed.
  • Partial failures with no route. A count of failures nobody can reach.
  • Selection cleared on page change. People stop paging rather than lose it.
  • Confirming the reversible. A click charged forever, dismissed by habit.
  • Blocking on a long batch. A spinner where a background job belonged.

Frequently Asked Questions