Toasts
Short-lived notifications (success, error, info) that don’t block the UI. Global trigger, stacked display, and accessible announcements.
The problem I keep seeing
After a save, delete, or error, the user needs feedback. Inline messages clutter the form; modals are heavy. You want a small, non-blocking notification (toast) that appears briefly and can be triggered from anywhere—mutations, API errors, background jobs—without passing callbacks through the tree.
Naive approach
Local state in the component that performs the action: toast message + a timeout to clear it. The toast is rendered in the same component; other parts of the app can’t trigger toasts without prop drilling or duplicating UI.
First improvement
A toast context (or small store): expose a function like toast(message) and render a list of toasts in a single fixed container (e.g. bottom-right). Any component can call the function; toasts stack and auto-dismiss after a delay.
Remaining issues
- Accessibility: Toasts should be announced to screen readers (
aria-liveregion) and not steal focus from the flow. - Variants: Success (green), error (red), loading, promise (loading → success/error). A small API keeps call sites clean.
- Stacking and dismiss: Multiple toasts should stack; user may want to dismiss one or pause auto-remove.
Production pattern
Use a library: sonner, react-hot-toast, or Radix UI Toast. They provide a global toast.success() / toast.error() (and often toast.promise() for async), a single Toaster in the layout, stacking, dismiss, and aria-live. Add the provider once; call from any component or from non-React code (e.g. API client) if the library supports an imperative API.
When I use this
- Mutation feedback: “Saved”, “Deleted”, “Failed to save” after a button or form submit.
- Background jobs: “Export ready”, “Sync complete” when you can’t show inline.
- Skip when: The message is critical and must be acknowledged (use a modal or inline error). For validation errors, prefer inline near the field.
Gotchas
- Don’t toast validation errors: Show those next to the fields or at the top of the form so the user can fix them; toasts are for success or non-field errors.
- aria-live: Use
aria-live="polite"so screen readers announce the toast without interrupting; avoidassertiveunless it’s urgent. - Promise toasts:
toast.promise(fn, { loading, success, error })keeps the UX to one toast that updates; better than loading toast + separate success toast.