ReactState ManagementAnimationsPortals

Build a Toast / Notification System

Learn how to build a reusable toast notification system with auto-dismiss, stacking, severity types, and smooth animations.

30 min read7 sections
01

Problem Statement

Build a toast notification system that supports:

  • Multiple toast types: success, error, warning, info
  • Auto-dismiss after a configurable duration
  • Manual dismiss via close button
  • Stacking multiple toasts simultaneously
  • Smooth enter and exit animations
  • Pause auto-dismiss on hover (bonus)

Why this question?

Toast systems test your understanding of component lifecycle, timer management, state arrays, animations, and portal rendering. It's a common interview question because it touches many React fundamentals in a compact problem.

02

Component Architecture

A clean toast system typically has three parts:

Architecturetext
ToastPage (or any parent)
  ├── State: Toast[] array
  ├── addToast(type, message) → appends to array
  ├── removeToast(id) → filters from array
  └── ToastContainer
       └── ToastN)
            ├── Icon (based on type)
            ├── Message text
            ├── Close button
            └── Auto-dismiss timer (setTimeout)

Interview tip

Interviewers like seeing a clear separation between the toast manager (state + add/remove logic) and the individual toast component (rendering + timer). This shows you think in composable pieces.

03

Toast State Management

Each toast needs a unique ID, a type, a message, and a duration:

typestypescript
type ToastType = "success" | "error" | "warning" | "info";

interface Toast {
  id: string;
  type: ToastType;
  message: string;
  duration: number; // ms, default 3000
}
state managementtypescript
const [toasts, setToasts] = useState<Toast[]>([]);

const addToast = (type: ToastType, message: string, duration = 3000) => {
  const id = crypto.randomUUID();
  setToasts(prev => [...prev, { id, type, message, duration }]);
};

const removeToast = (id: string) => {
  setToasts(prev => prev.filter(t => t.id !== id));
};
04

Auto-Dismiss with Timers

Each toast should start a timer when it mounts and clean it up when it unmounts:

useEffect timer patterntypescript
useEffect(() => {
  const timer = setTimeout(() => {
    onDismiss(toast.id);
  }, toast.duration);

  return () => clearTimeout(timer);
}, [toast.id, toast.duration, onDismiss]);

Pause on hover

For the bonus "pause on hover" feature, track remaining time with a ref. On mouseenter, clear the timeout and save the remaining ms. On mouseleave, start a new timeout with the remaining time.

05

Animations & Transitions

CSS transitions or keyframe animations work well for toast enter/exit. A common pattern:

animation approachtext
Enter: translateX(100%) → translateX(0) with opacity 01
Exit:  translateX(0) → translateX(100%) with opacity 10

Use a state flag like "isExiting" to trigger the exit animation
before actually removing from the array (setTimeout for animation duration).
06

Accessibility

Toasts should be accessible to screen readers:

  • Use role="alert" or role="status" on toast elements
  • Use aria-live="polite" for info/success, aria-live="assertive" for errors
  • Ensure close buttons have aria-label
  • Don't rely solely on color to convey toast type — use icons too
07

Interview Follow-up Questions

Q:How would you implement a global toast system accessible from any component?

A: Use React Context to provide addToast/removeToast functions. Wrap the app in a ToastProvider that manages the state and renders the ToastContainer via a portal.

Q:How do you prevent memory leaks with toast timers?

A: Always clear timeouts in the useEffect cleanup function. If a toast is manually dismissed before the timer fires, the cleanup ensures the stale timeout doesn't try to update unmounted state.

Q:How would you limit the number of visible toasts?

A: Maintain a queue. Only render the first N toasts from the array. When one is dismissed, the next in queue slides in. This prevents the screen from being overwhelmed.

Ready to build it yourself?

We've set up the toast container and trigger buttons. Implement the state management, auto-dismiss timers, and animations from scratch.