Toast Notifications Are Where Good UX Goes to Die
Toasts are the default answer for every ephemeral message, and that's exactly the problem. Here's a breakdown of when toasts earn their keep, when they fail users, and what to use instead.

Every product team eventually ships a toast notification system, and most of them get used for everything from "saved successfully" to "your payment failed, please contact support." That's a design smell. Toasts are a specific tool for a specific job, and treating them as a universal feedback mechanism is how you end up with users missing critical errors and screen reader users getting ambushed by announcements they can't dismiss.
What a toast is actually for
A toast — sometimes called a snackbar, depending on which design system you grew up with — is a transient, non-modal message that appears, lingers for a few seconds, and disappears. The key properties:
- Transient: it goes away on its own
- Non-modal: it doesn't block the UI
- Low priority: dismissing it or missing it entirely should be fine
That last point is the one teams forget. If missing the message has consequences — the user needs to retry something, confirm an action, or understand why a workflow failed — a toast is the wrong container. You've chosen a disappearing message to communicate something that can't afford to disappear.
The one-sentence test
Before you reach for a toast, ask: if the user looks away for ten seconds and misses this entirely, is that okay? If the answer is yes, toast is fine. If the answer is "well, they'll figure it out eventually," you're already in trouble.
The four kinds of feedback, and which deserve a toast
Most UI feedback falls into four buckets. Only one of them is a good match for toasts.
1. Confirmation of a completed, reversible action
This is the toast's natural home. "Draft saved." "Message sent." "Added to cart." The user did a thing, the thing worked, and they already know what they did because they just did it. The toast is a soft confirmation, not information transfer.
Bonus points if you include an undo action in the toast — this is where the pattern genuinely shines. Gmail's "Message sent — Undo" is the canonical example, and it works because the undo is only valuable within a few seconds anyway.
2. Inline validation and form errors
Never a toast. Errors on a specific field belong next to that field, period. A toast that says "Please fix the errors in your form" is a UX war crime: it tells the user something is wrong without telling them what or where, and then it disappears.
3. Critical errors and blocking states
Also never a toast. "Your session expired," "Payment failed," "We couldn't save your changes" — these need persistent surfaces. A banner at the top of the view, an inline error in context, or a modal if the user truly cannot proceed. If the user needs to make a decision based on the message, they need the message to stay put.
4. Background system events
A new message arrived, a long-running job finished, someone shared a document with you. These are candidates for toasts, but they're also candidates for a persistent notification center. Our rule of thumb: if it happens more than once or twice per session, it needs a persistent record somewhere, with the toast as an optional heads-up.
The accessibility problem nobody talks about
Toasts are hostile to assistive tech by default. Here's why:
- Screen readers may not announce them unless you wire up
aria-livecorrectly - Keyboard users often can't reach them without a dedicated shortcut
- Users with cognitive disabilities, low vision, or motor impairments can't always read and act within a 4-second window
- WCAG 2.2 Success Criterion 2.2.1 (Timing Adjustable) applies here — if your toast disappears on a timer, that timer needs to be adjustable or dismissible
At minimum, your toast container needs an appropriate live region:
<div
role="status"
aria-live="polite"
aria-atomic="true"
class="toast-region"
>
<!-- toast nodes injected here -->
</div>
For errors that need more urgency, use role="alert" and aria-live="assertive" — but sparingly, because assertive announcements interrupt whatever the screen reader was saying. Interrupting a user mid-sentence to announce "Draft saved" is worse than not announcing it at all.
Dismissal and timing
Every toast needs a visible close button. Not an X that appears on hover — hover doesn't exist on touch, and we've been over that. A persistent, keyboard-focusable dismiss control. If your toast auto-dismisses, pause the timer when the user hovers, focuses, or when a screen reader has focus in the region. Most mature toast libraries do this; verify yours does.
A reasonable default range for auto-dismiss, in our experience, is 4–7 seconds for short confirmations and never for anything with an action button. If there's an "Undo" the user might want to click, give them time to read the message, process it, and move the mouse — closer to 8–10 seconds, and pause on hover.
Stacking, queueing, and the chaos problem
What happens when five toasts fire at once? Most apps either stack them (and overwhelm the screen) or queue them (and make the user wait through a slideshow of messages they've already mentally processed).
Some patterns that have worked for us:
- Cap the stack: show the most recent 3, collapse the rest into a "+2 more" affordance
- Deduplicate: if the same message fires twice within a short window, don't show it twice
- Group by type: "3 items added to cart" beats three separate "added to cart" toasts
- Prioritize: errors shouldn't be buried behind a queue of success confirmations
Here's a sketch of a dedup-aware enqueue function:
type Toast = {
id: string;
message: string;
variant: 'success' | 'error' | 'info';
createdAt: number;
};
const DEDUP_WINDOW_MS = 2000;
function enqueueToast(queue: Toast[], next: Omit<Toast, 'id' | 'createdAt'>) {
const now = Date.now();
const recent = queue.find(
(t) =>
t.message === next.message &&
t.variant === next.variant &&
now - t.createdAt < DEDUP_WINDOW_MS
);
if (recent) return queue;
return [
...queue.slice(-2), // keep last 2, drop older
{ ...next, id: crypto.randomUUID(), createdAt: now },
];
}
Small thing, big quality-of-life win. Users doing bulk actions won't get buried.
Positioning: boring choices are correct choices
There's a design-review ritual where someone argues toasts should appear top-right because that's "where notifications live." Then someone else argues bottom-center because that's where mobile apps put them. Then someone picks top-center because it's "more visible."
The honest answer: pick one, document it in your design system, and stop re-litigating. A few constraints that matter more than the specific position:
- Don't cover primary actions or form fields
- On mobile, bottom placement risks collision with the OS gesture area and the keyboard — top is usually safer
- Toasts should not shift page content when they appear
- Respect safe areas on iOS and the Dynamic Island zone
One consistent position across your product beats a clever contextual position that nobody can predict.
When to retire the toast entirely
Sometimes the best toast is no toast. Inline feedback — a subtle checkmark next to the save button, a field that briefly flashes green, a count that updates — carries the same information with less interruption. If your product has a lot of auto-saving, auto-syncing, and background activity, consider a persistent status indicator (think of the "All changes saved" text in Google Docs) rather than a stream of toasts announcing each save.
The general heuristic: toasts interrupt, inline feedback informs. Interruption should be rare.
Where we'd start
If you're auditing an existing product, pull every toast(...) or equivalent call in the codebase into a spreadsheet. For each one, ask: is this a confirmation of a user-initiated reversible action? If not, it probably belongs somewhere else — inline, in a banner, in a notification center, or deleted entirely. In our client work, this exercise typically cuts toast usage by half and surfaces at least two or three error paths that were silently failing because the toast was the only signal and users were missing it.
Then, make sure the ones that survive are accessible: live region wired up correctly, dismiss button present, timer pauses on hover and focus, errors use the right role. That's the baseline. Everything else — animation style, stack behavior, position — is polish on top of a system that actually serves users.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading

Modal Dialogs Are Still Broken in 2026: A Field Guide to Getting Them Right
Modals look trivial until you ship one. Focus traps leak, ESC closes the wrong thing, screen readers announce chaos. Here's what we check before a dialog gets within a mile of production.

Form Validation Timing: When to Show Errors Without Being a Jerk
Validate too early and you shout at users mid-keystroke. Validate too late and they fill out the whole form before learning it's wrong. Here's the timing model we actually ship.

Disabled Buttons Are a UX Smell: What to Do Instead
Greyed-out buttons feel safe to designers and devs, but they punish users for your form's problems. Here's a pattern-by-pattern replacement guide that keeps buttons alive and errors honest.
