javascript 111 lines · 3 tabs

Toast Notification Queue in React with Context and useReducer

Shared by codesnips Aug 2026
3 tabs
export const MAX_VISIBLE = 4;

const DEFAULTS = {
  variant: "info",
  duration: 4000,
};

export function createToast(payload) {
  return {
    id: crypto.randomUUID(),
    message: "",
    ...DEFAULTS,
    ...payload,
  };
}

export function toastReducer(state, action) {
  switch (action.type) {
    case "ADD": {
      const next = [action.toast, ...state];
      return next.slice(0, MAX_VISIBLE);
    }
    case "REMOVE":
      return state.filter((t) => t.id !== action.id);
    case "CLEAR":
      return [];
    default:
      return state;
  }
}
3 files · javascript Explain with highlit

A toast system needs to solve two problems at once: any component anywhere in the tree must be able to fire a notification, and the notifications themselves must be managed as a bounded, self-expiring queue. This snippet models that with a single reducer-backed store exposed through context, plus a small headless renderer that reads the queue and dismisses entries on their own timers.

In toastReducer.js, the state is just an ordered array of toast objects. The ADD action prepends a new toast and then applies MAX_VISIBLE by slicing the tail, which caps how many toasts can pile up during a burst of activity — older ones are silently dropped rather than allowed to grow without bound. Each toast carries an id created with crypto.randomUUID() so that REMOVE can target a specific entry regardless of its position, and variant/duration are normalized with defaults at creation time so the renderer never has to guess. Keeping this logic in a pure reducer makes the transitions easy to reason about and trivial to test in isolation.

ToastContext.jsx wires the reducer into the tree with useReducer and splits the exposed API into stable dispatch-derived callbacks. The show function is wrapped in useCallback so its identity stays stable across renders — important because consumers often list it as a useEffect dependency. The provider value is memoized with useMemo to avoid re-rendering every consumer whenever the toast array changes. A convenience useToast hook throws when used outside the provider, which turns a silent no-op into an obvious developer error.

ToastViewport.jsx is the presentational layer. It maps over toasts and, critically, each ToastItem owns its own dismissal timer via useEffect, clearing it on unmount so a manually-dismissed toast never fires a stale remove. A duration of 0 opts a toast out of auto-dismiss for things like errors that require acknowledgement. The container uses role="status" and aria-live="polite" so screen readers announce new messages without stealing focus. Reaching for this pattern makes sense once toasts are triggered from many unrelated places; the reducer centralizes queue rules while per-item timers keep lifecycle logic local and leak-free.


Related snips

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Semantic HTML Example</title>

Semantic HTML5 elements and accessibility best practices

html html5 semantics
by Alex Chang 2 tabs
typescript
import axios, { AxiosError } from 'axios'
import { v4 as uuidv4 } from 'uuid'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api/v1',
  timeout: 15000,

Axios API client with interceptors

react axios api
by Maya Patel 1 tab
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface FilterState {
  search: string
  category: string | null

Zustand for lightweight state management

react zustand state-management
by Maya Patel 2 tabs
swift
import SwiftUI

struct ContentView: View {
    @State private var username = ""
    @State private var isLoggedIn = false
    @StateObject private var viewModel = LoginViewModel()

SwiftUI declarative UI with state management

swift swiftui ios
by Sofia Martinez 2 tabs
html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Form Validation Example</title>
  <style>

HTML forms with validation and accessibility

html forms validation
by Alex Chang 1 tab
javascript
import { Application } from "@hotwired/stimulus"
import FormSubmitController from "./controllers/form_submit_controller"

const application = Application.start()
application.debug = false

Disable submit button while Turbo form is submitting

rails hotwire stimulus
by codesnips 3 tabs

Share this code

Here's the card — post it anywhere.

Toast Notification Queue in React with Context and useReducer — share card
Link copied