import { createContext } from "react";
export type ToastVariant = "info" | "success" | "error";
export interface Toast {
id: string;
message: string;
variant: ToastVariant;
duration: number;
}
export type ToastAction =
| { type: "ADD"; toast: Toast }
| { type: "REMOVE"; id: string };
export function toastReducer(state: Toast[], action: ToastAction): Toast[] {
switch (action.type) {
case "ADD":
return [...state, action.toast];
case "REMOVE":
return state.filter((t) => t.id !== action.id);
default:
return state;
}
}
export interface ToastContextValue {
toasts: Toast[];
push: (message: string, opts?: { variant?: ToastVariant; duration?: number }) => string;
dismiss: (id: string) => void;
}
export const ToastContext = createContext<ToastContextValue | null>(null);
import { useCallback, useEffect, useReducer, useRef } from "react";
import { createPortal } from "react-dom";
import { ToastContext, toastReducer, type ToastVariant } from "./ToastContext";
import { ToastViewport } from "./ToastViewport";
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, dispatch] = useReducer(toastReducer, []);
const timers = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map());
const dismiss = useCallback((id: string) => {
const handle = timers.current.get(id);
if (handle) {
clearTimeout(handle);
timers.current.delete(id);
}
dispatch({ type: "REMOVE", id });
}, []);
const push = useCallback(
(message: string, opts?: { variant?: ToastVariant; duration?: number }) => {
const id = crypto.randomUUID();
const duration = opts?.duration ?? 4000;
dispatch({
type: "ADD",
toast: { id, message, variant: opts?.variant ?? "info", duration },
});
timers.current.set(
id,
setTimeout(() => dismiss(id), duration)
);
return id;
},
[dismiss]
);
useEffect(() => {
const active = timers.current;
return () => {
active.forEach(clearTimeout);
active.clear();
};
}, []);
return (
<ToastContext.Provider value={{ toasts, push, dismiss }}>
{children}
{createPortal(<ToastViewport toasts={toasts} onDismiss={dismiss} />, document.body)}
</ToastContext.Provider>
);
}
import { useContext } from "react";
import { ToastContext, type ToastContextValue } from "./ToastContext";
export function useToast(): ToastContextValue {
const ctx = useContext(ToastContext);
if (!ctx) {
throw new Error("useToast must be used within a <ToastProvider>");
}
return ctx;
}
import type { Toast } from "./ToastContext";
interface ViewportProps {
toasts: Toast[];
onDismiss: (id: string) => void;
}
function ToastItem({ toast, onDismiss }: { toast: Toast; onDismiss: (id: string) => void }) {
return (
<div role="status" className={`toast toast--${toast.variant}`}>
<span className="toast__message">{toast.message}</span>
<button
type="button"
className="toast__close"
aria-label="Dismiss notification"
onClick={() => onDismiss(toast.id)}
>
×
</button>
</div>
);
}
export function ToastViewport({ toasts, onDismiss }: ViewportProps) {
if (toasts.length === 0) return null;
return (
<div className="toast-viewport" aria-live="polite" aria-relevant="additions">
{toasts.map((toast) => (
<ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />
))}
</div>
);
}
This snippet shows a self-contained toast notification system built around React context, a reducer, and a portal. The design goal is a single global queue that any component can push to without prop-drilling, while keeping each toast's auto-dismiss timer isolated and cancellable.
In ToastContext, the state is modeled as an append-only array of Toast objects reduced by toastReducer. Using a reducer instead of raw useState keeps the mutation logic (ADD, REMOVE) centralized and pure, which matters once timers, manual dismissal, and possible future actions like UPDATE are all touching the same list. The ToastContextValue splits the API into toasts (read state) and imperative push/dismiss methods so the provider owns the queue and consumers only trigger transitions.
ToastProvider wires the reducer to the DOM. The push function is wrapped in useCallback and generates an id with crypto.randomUUID() so repeated renders don't produce new function identities and break memoized children. Each toast schedules its own removal with setTimeout, and the timeout handle is tracked in a timers ref keyed by id; dismiss clears that handle before dispatching REMOVE so a manually-closed toast can't fire a duplicate removal later. The cleanup effect clears every outstanding timer on unmount, which prevents the classic 'setState on unmounted component' leak. Rendering goes through createPortal into document.body, keeping toasts above the normal stacking context regardless of where the provider sits in the tree.
useToast is a thin consumer hook that reads the context and throws when used outside the provider — a deliberate fail-fast guard that turns a silent undefined into an obvious developer error. The ToastViewport and ToastItem components in ToastViewport are pure presentational pieces driven entirely by props, so styling and behavior stay decoupled.
The trade-off is that all toasts share one queue and one provider instance; that is exactly what makes the push API ergonomic from anywhere. A pitfall to watch is duplicate toasts from rapid events — callers that need dedupe should pass a stable key, since this queue intentionally allows repeats. This pattern is the right reach whenever transient, app-wide feedback is needed without coupling it to any particular route or component.
Related snips
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
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
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
interface FilterState {
search: string
category: string | null
Zustand for lightweight state management
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
import React from "react";
type FallbackProps = {
error: Error;
reset: () => void;
};
React Error Boundary + error reporting hook
<h1>Products</h1>
<%= form_with url: products_path, method: :get,
data: { turbo_frame: "products_list", turbo_action: "advance" } do |f| %>
<div class="filters">
<%= f.text_field :q, value: params[:q], placeholder: "Search products" %>
Frame navigation that targets a specific frame via form_with
Share this code
Here's the card — post it anywhere.