VayuUI

Toaster

A notification component with imperative API, stacked animations, and custom toast support using compound components.

Installation

npx vayu-ui-cli@latest add toaster

Usage

Wrap your app with ToastProvider, then use the useToast hook anywhere to trigger toasts.

Standard Variants

With Actions

Promise & Loading

Duration Control

Positions

Stack Test

Custom Toast (Compound)

Non-Dismissible

With Custom Icon

{/* Setup: add ToastProvider to your root layout */}
<ToastProvider>
  <App />
</ToastProvider>

{/* Trigger toasts from any component */}
function Page() {
  const toast = useToast();

  return (
    <Button onClick={() => toast.success('Saved', { description: 'Changes applied.' })}>
      Save
    </Button>
  );
}

{/* Promise-based toasts */}
await toast.promise(fetchData(), {
  loading: 'Fetching...',
  success: 'Data loaded!',
  error: 'Fetch failed',
});

{/* Custom content with compound components */}
toast.custom(
  <div className="flex items-start gap-3">
    <Toast.Title>Update available</Toast.Title>
    <Toast.Description>A new version is ready.</Toast.Description>
    <Toast.Close onClick={() => toast.removeToast(id)} />
  </div>,
);

Anatomy

import { ToastProvider, useToast, Toast } from 'vayu-ui';

{/* Provider — renders the portal and stack */}
<ToastProvider defaultPosition="bottom-right" defaultDuration={5000}>
  <App />
</ToastProvider>

{/* Hook — imperative API */}
const toast = useToast();
toast.success('Message', { description: 'Details' });
toast.error('Message', { action: { label: 'Retry', onClick: handleRetry } });
toast.promise(promise, { loading: '...', success: '...', error: '...' });
toast.custom(<Toast.Title>Custom</Toast.Title>);

{/* Compound sub-components for custom toasts */}
<Toast.Title>Title</Toast.Title>
<Toast.Description>Description</Toast.Description>
<Toast.Close />
  • ToastProvider — Context provider that renders the toast portal and manages state. Wrap your app once at the root.
  • useToast() — Hook returning imperative methods: success, error, warning, info, loading, promise, custom, updateToast, removeToast.
  • Toast.Title — Title text for custom toast content.
  • Toast.Description — Body text for custom toast content.
  • Toast.Close — Dismiss button for custom toast content.

Accessibility

  • Keyboard Support:
    • Escape — Dismiss the first dismissible toast in the stack.
    • Tab — Move focus between toasts and interactive elements (action buttons, close buttons).
    • Enter / Space — Activate the focused action button or expand the stack via the "+N more" badge.
  • ARIA Attributes:
    • role="status" on success, info, and loading toasts.
    • role="alert" on error and warning toasts.
    • aria-live="polite" for non-critical notifications; aria-live="assertive" for critical ones.
    • aria-atomic="true" ensures the full toast content is announced as a unit.
    • aria-label on close buttons (e.g. "Dismiss success notification") and notification regions.
    • role="progressbar" on the countdown bar with aria-valuemin and aria-valuemax.
    • All icons are marked aria-hidden="true".
  • Focus Behavior:
    • Individual toasts are focusable (tabIndex={0}) with visible focus rings.
    • Hovering or focusing the stack pauses all toast timers and expands collapsed toasts.
    • Focus-visible rings on action buttons, close buttons, and the "+N more" badge.

Screen reader behavior

When a toast appears, the screen reader announces it based on its type. Success and info toasts use a polite live region (role="status"), so they are announced after the current speech finishes. Error and warning toasts use an assertive live region (role="alert"), interrupting the current speech immediately. The toast content is announced atomically — the type label (e.g. "Success notification"), followed by the title and description. Action buttons are announced as interactive elements. When a toast is dismissed, the removal is communicated via the live region. The close button is labeled descriptively (e.g. "Dismiss error notification"). The countdown progress bar uses role="progressbar" to indicate time remaining before auto-dismiss.

Component Folder Structure

Toaster/
├── index.ts              # Re-exports all components, hook, and types
├── types.ts              # TypeScript type definitions
├── constants.ts          # Style maps, position classes, layout constants
├── ToastProvider.tsx      # Context provider with state management and imperative API
├── ToastContainer.tsx     # Portal root and position-based toast grouping
├── ToastStack.tsx         # Sonner-style stack with expand/collapse and animations
├── ToastItem.tsx          # Individual toast with timer, swipe-to-dismiss, and progress bar
├── ToastUtilities.tsx     # Compound sub-components (Title, Description, Close)
├── ToastIcons.tsx         # Inline SVG icons for each toast type
└── README.md              # Component usage reference

Props

ToastProvider

PropTypeDefaultDescription
childrenReactNodeApp content wrapped by the provider.
defaultPositionToastPosition"bottom-right"Default position for all toasts.
maxToastsnumber5Maximum number of toasts visible at once.
defaultDurationnumber5000Default auto-dismiss duration in ms.

useToast() Methods

MethodArgumentsReturnsDescription
toast.success(message, options?)stringShows a success toast. Returns toast ID.
toast.error(message, options?)stringShows an error toast. Returns toast ID.
toast.warning(message, options?)stringShows a warning toast. Returns toast ID.
toast.info(message, options?)stringShows an info toast. Returns toast ID.
toast.loading(message, options?)stringShows a loading toast (no auto-dismiss).
toast.promise(promise, messages, options?)Promise<T>Handles loading/success/error states for a promise.
toast.custom(content, options?)stringRenders custom JSX content in a toast.
toast.updateToast(id, options)voidUpdates an existing toast by ID.
toast.removeToast(id)voidRemoves a toast by ID.

ToastOptions

OptionTypeDefaultDescription
titleReactNodePrimary heading text.
descriptionReactNodeSecondary body text.
durationnumber5000Duration in ms before auto-dismiss. Set to 0 for persistent toasts.
positionToastPositionPosition override for this toast.
action{ label: ReactNode, onClick: () => void }Optional action button inside the toast.
dismissiblebooleantrueWhether the close button is visible.
iconReactNodeCustom icon to replace the default type icon.
customContentReactNodeCustom content rendering (used by toast.custom).
onClose() => voidCallback when the toast is dismissed.

Toast.Title

PropTypeDefaultDescription
childrenReactNodeTitle content.
classNamestringAdditional CSS classes.

Toast.Description

PropTypeDefaultDescription
childrenReactNodeDescription content.
classNamestringAdditional CSS classes.

Toast.Close

PropTypeDefaultDescription
onClick() => voidClick handler for dismiss.
aria-labelstring"Dismiss notification"Accessibility label.
classNamestringAdditional CSS classes.

On this page