Frontend Handbook
01Built

Toast / Notification System

Queued, auto-dismissing notifications with pause on hover and stacking.

How it works

Toasts are short messages that pop up in a corner, stack, disappear on their own, and can carry an action like Undo or follow a slow request from “Saving…” to its result. They look simple, but the pattern combines app-wide state, timers that pause and resume, a queue, motion and screen-reader announcements in one small surface.

  • One store, no provider: any code can call toast.success(), and toasts survive navigation.
  • Fair timers: a toast is visible for its full duration of time you could actually see it.
  • A queue, not a pile: only a few toasts show at once; the rest wait their turn.
  • Announced, never focus-stealing: screen readers hear every toast; errors more urgently.
Read more

Where you'd use it

  • Confirming an action that has no page of its own: “Changes saved”, “Link copied”.
  • Offering a short way back: “Invoice deleted · Undo”.
  • Following background work: an upload or save that moves from loading to done or failed.
  • App-wide notices from anywhere: a connection lost toast raised by a data layer, not a page.

How we built it

A small store lives outside React and holds every toast. Components subscribe to it with useSyncExternalStore, and the toast area is mounted once in the playground layout, outside each demo's error boundary, so a crashing demo can't take the toasts down and other practices reuse the same system.

Each toast remembers how much time it has left. Hover, keyboard focus and a hidden tab are tracked as separate pause reasons; the timer only runs when none of them apply, then continues with what was left. A visible limit sends extra toasts to a first-in, first-out queue, and identical toasts merge into one with a count.

toast.promise() keeps one toast and updates it in place from loading to success or error, with a 30 s timeout. Two hidden live regions do the announcing, so the toasts themselves never need focus. Motion is CSS only: @starting-style to enter, a collapsing grid row to exit.

Best practices

  • Do: Keep errors and toasts with an action on screen until dismissed.

    Avoid: Timing out something the user must read or act on (WCAG 2.2.1).

  • Do: Pause timers on hover, keyboard focus and a hidden tab, and resume with the remaining time.

    Avoid: Restarting the full timer, or letting a toast vanish while someone is reading it.

  • Do: Announce through a polite region, and an assertive one only for errors.

    Avoid: Making every toast a live region or moving focus to it: content gets read twice and typing is interrupted.

  • Do: Update one toast in place for async work.

    Avoid: Stacking “Saving…”, then “Saved” as separate toasts.

  • Do: Give keyboard users a way in and out: F8 to reach the newest toast, Escape to dismiss.

    Avoid: Toasts only reachable with a mouse.

Try it with the demo controls

Success / Error / Info / Warning
Errors stay until dismissed; the others count down (watch the thin bar). Hover or press F8 and the countdown stops.
With action
The Undo toast never times out. Pressing Undo runs once, then the toast closes.
Burst (10)
Only “Max visible” toasts show; the rest wait behind a “+N more” count and appear as others close.
Dismiss all
Visible and queued toasts all go, without errors.
Position
Six corners and edges; the newest toast is always nearest the screen edge.
Duration
Applies to new toasts only; toasts already on screen keep their time.
Max visible
Lowering it sends the extra toasts back to the front of the queue instead of dropping them.
Delete with Undo
A real use case: the item disappears, and Undo brings it back.
Track a request + Save latency / failure rate
One toast goes from loading to success or error in place. Try a high failure rate, or leave the page while it's loading: the toast still updates.

Trade-offs and limits

  • A list with a queue instead of a collapsed “deck”: predictable and easier to make accessible, with less animation flair.
  • No swipe-to-dismiss: the close button, Escape and the timeout cover it.
  • No history or notification center, and toasts don't survive a full reload: they are transient by design.
  • Text only (title, description, one action), to keep the focus on behavior.

Demo controls

Delete with Undo

  • Invoice #1042
  • Invoice #1043
  • Invoice #1044
  • Invoice #1045

Track a request

Sends a real request to the mock API. One toast follows it from loading to the result.