React cookbook · Beginner
Show toast notifications from anywhere
Display success and error messages from any component or mutation without prop drilling.
Approach
Create a small store (context + reducer or Zustand) with show()/dismiss() actions, render a toast region once near the root with role="status"/aria-live, and auto-dismiss after a timeout (pausing on hover). Expose a toast helper for non-component code like mutation callbacks.
Solution
import { create } from "zustand";
type Toast = { id: number; kind: "success" | "error"; message: string };
type ToastState = { toasts: Toast[]; show: (kind: Toast["kind"], message: string) => void; dismiss: (id: number) => void };
let nextId = 1;
export const useToasts = create<ToastState>()((set, get) => ({
toasts: [],
show: (kind, message) => {
const id = nextId++;
set((s) => ({ toasts: [...s.toasts, { id, kind, message }] }));
setTimeout(() => get().dismiss(id), 5000);
},
dismiss: (id) => set((s) => ({ toasts: s.toasts.filter((t) => t.id !== id) })),
}));
export const toast = {
success: (m: string) => useToasts.getState().show("success", m),
error: (m: string) => useToasts.getState().show("error", m),
};
export function ToastRegion() {
const { toasts, dismiss } = useToasts();
return (
<div className="toast-region" role="status" aria-live="polite">
{toasts.map((t) => (
<div key={t.id} className={`toast toast-${t.kind}`}>
{t.message}
<button aria-label="Dismiss notification" onClick={() => dismiss(t.id)}>×</button>
</div>
))}
</div>
);
}
Pitfalls
- Toasts as the only place important errors appear (they vanish).
- Not announcing toasts to screen readers.
- Stacking dozens of identical toasts — dedupe by message.
Variations
- Use
sonnerorreact-hot-toastlibraries. - Use
role="alert"(assertive) for errors that need immediate attention.
Further reading: MDN: ARIA status role
Reference: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/status_role
More React recipes
- Add optimistic updates to a todo list
- Build an accessible modal dialog
- Build infinite scroll with TanStack Query
- Call a cookie-authenticated API with CSRF protection
- Debounce a search input in React
- Handle errors gracefully with error boundaries
- Implement dark mode that follows the system
- Poll a long-running job's status