How Should a SaaS App Handle Bulk Actions?
How should a SaaS app handle bulk actions?
With an honest selection model, an undo instead of a confirmation wherever the action can be reversed, visible progress while it runs, and a per-item result when it finishes. Most bulk action bugs are not code bugs. They come from an interface that let a person select one thing and act on another.
We design product interfaces alongside the marketing sites we build, and bulk actions are where we see the most expensive small mistakes. A filter that quietly resets, a select-all that means something different than the user thinks, a progress bar that finishes while half the work silently failed. Each one is a support ticket at best and a data loss incident at worst.
This is the framework we use, in the order the user experiences it.
Why are bulk actions harder than they look?
Because they break the normal guarantee of a user interface, which is that what you see is what you act on. A single row action is safe: you clicked the row, you saw the row, the app changed the row. A bulk action separates the thing you saw from the thing you changed, and everything between those two moments is where the risk lives.
The gap widens with every feature you add. Filters change which rows exist. Pagination changes which rows are visible. Live data changes which rows are current. Sorting changes which rows are where. Any of those can shift between selection and execution.
So the design problem is not the button. It is keeping a promise about a set of items across time, across pages, and across a data source that is still moving.
How should selection itself work?
Selection needs to survive the things users naturally do and reset on the things that change meaning. Our rule: scrolling and sorting preserve selection, changing a filter or a search query clears it.
That sounds arbitrary but it follows from intent. Sorting is a view change, and a user who sorts still means the same twelve records. Filtering is a set change, and a user who filters has said out loud that they are now thinking about a different set. Preserving selection across a filter change is how you get someone deleting rows they last saw two minutes ago.
Always show the count, always keep it visible, and never make the user scroll to find out how many things are selected. A persistent bar with the number and the available actions is the pattern that works, and it is worth the vertical space it costs. The underlying table matters here too, and we went through that in designing web data tables people can actually use.
What does select all actually mean?
This is the single most dangerous control in a SaaS app, because it has two plausible meanings and users pick whichever one suits them. It can mean everything on this page, or everything matching the current filter, and those numbers can differ by four orders of magnitude.
The pattern that works is to do both, explicitly, in two steps. The header checkbox selects the visible page and says so, with the count. Then a second line appears offering to select all items matching the filter, again with the count. The user opts into the bigger meaning, and they see the number before they do.
The number is the safety mechanism. A person who ticks a box and reads "50 selected" behaves differently from one who reads "12,480 selected", and no amount of warning copy substitutes for showing them the figure. If you cannot compute the total cheaply, show an approximate count and label it as approximate rather than hiding it.
Should a bulk action confirm, or offer undo?
Undo, wherever the action can be reversed. Jakob Nielsen of Nielsen Norman Group has argued this for years, and the reasoning is about attention rather than politeness. His warning is direct: if you cry wolf too many times, people will stop paying attention to the question, and the confirmation dialog will lose its power to prevent errors.
He also names the failure mode in the copy itself. When users are asked whether they are sure they want to do this, without further details, the only sensible reaction is of course I want to do the thing I just told you to do, and to hit yes.
Nielsen Norman Group reserves confirmation for actions with serious consequences, such as destroying users' work or costing large amounts of money, and requires that the dialog restate the request and explain what is about to happen with specific information. That specificity is the test. If your dialog does not name the count and the action, it is decoration.
| Bulk action | Pattern | Why |
|---|---|---|
| Archive 40 records | Undo toast, no dialog | Fully reversible in one click |
| Change owner on 200 records | Undo toast, no dialog | Reversible, previous value known |
| Permanently delete 200 records | Dialog naming the count | Not reversible |
| Email 12,000 contacts | Dialog naming the count and the list | Leaves your system, not reversible |
| Export to CSV | Neither | No side effect at all |
What should happen while the action runs?
Tell the truth about duration and let the user leave. A bulk action on 12,000 records is a job, not a click, and pretending otherwise produces a spinner that holds someone hostage for ninety seconds.
Below roughly two seconds, an inline spinner on the action button is fine. Above that, start the job, show a progress indicator with a real count of items completed, and let the user navigate away while it continues. Notify them when it finishes, in the app rather than only by email.
Never show a progress bar that moves at a made-up rate. If you do not know the total, show a count of items processed instead. A number that is honest and imprecise beats a bar that is smooth and false, and users can tell the difference faster than teams expect. The related states are worth designing properly too, which we covered in getting loading and empty states right.
What happens when some items fail?
Partial failure is the normal case at scale, and most apps handle it badly by reporting a single success or a single error for the whole batch. Neither is true. The correct output is a per-item result the user can act on.
Say how many succeeded, how many failed, and why each failure happened. Then give one button to retry only the failures. That last part is what turns a frustrating screen into a usable one, because the alternative is a user manually reconstructing which 14 of 200 records did not update.
Do not roll back the successes to make the result clean. A partial success is a real state and users would rather keep 186 completed changes than lose them for tidiness. The exception is an action where a partial result is genuinely incoherent, and those are rarer than engineers assume.
How do you make bulk actions accessible?
Start with the checkboxes, because a table of tiny targets fails the basic requirement. WCAG 2.2 Success Criterion 2.5.8, Target Size (Minimum), is a Level AA requirement that the size of the target for pointer inputs is at least 24 by 24 CSS pixels, with exceptions for spacing, an equivalent control elsewhere, inline targets, user agent controlled sizing, and cases where the presentation is essential. Dense tables routinely fail this and it is usually a padding fix, not a redesign.
Then make the selection count reachable without sight. A count that only exists as visible text in a floating bar is invisible to a screen reader user who is still moving through rows. Announce changes to the selected count in a live region, and make sure the bulk action bar is in the tab order in a sensible place rather than appended at the end of the document.
Keyboard selection matters too. Shift-click range selection is standard on the mouse and frequently has no keyboard equivalent, which means a keyboard user selects two hundred rows one at a time. We went further into this territory in designing focus and accessibility into product UI.
Which bulk actions should you not build?
Any action whose failure mode you cannot explain in one sentence. If a bulk operation touches permissions, billing, or anything that sends messages outside your system, the design cost is much higher than the button suggests, and the right first version is often a smaller one.
We would also avoid building a bulk version of anything users do less than weekly. Bulk actions carry real complexity: selection state, job queues, partial failure, undo. Spending that on a rare action is how a roadmap fills with features nobody asked for while the table itself stays slow.
The honest test is to look at how people work around its absence today. If they are exporting to CSV, editing in a spreadsheet, and reimporting, that is a clear signal. If nobody is working around anything, you are guessing.
What does a good bulk action feel like?
Like nothing. The user selects a set, sees the count, presses a button, gets a toast with an undo, and moves on. No dialog, no waiting, no ambiguity about what was affected. The complexity all sits on your side, which is exactly where it belongs.
The teams that get here did not add features. They picked one honest selection model, replaced their confirmations with undo, and made partial failure a first class result. Those three decisions cover most of the ground, and they are cheaper to make at design time than to retrofit after the first incident.
If you are building or rebuilding a product interface and want a second opinion on the patterns, we are happy to walk through it. You can find us at phoenix.studio.
Want a site that performs like this?
Tell us about your project. We will come back with a clear next step, no pressure.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Have a project like this?
Tell us where you want to go. We'll tell you how we'd get you there.