Dinachi

Toast

A succinct message displayed temporarily to provide feedback about an action. Supports multiple variants, promise tracking, custom rendering, and global usage outside React.

View Source

#Installation

npx @dinachi/cli@latest add toast

#Usage

tsx
import {
  Toast,
  ToastProvider,
  ToastPortal,
  ToastViewport,
  ToastRoot,
  ToastContent,
  ToastTitle,
  ToastDescription,
  ToastAction,
  ToastClose,
  ToastPositioner,
  ToastArrow,
  ToastList,
  useToastManager,
  createToastManager,
} from '@/components/ui/toast'

#Examples

#Toast Manager

The useToastManager hook (inside React) and createToastManager factory (outside React) return the same manager object.

#Methods

#add(options)

Create a toast. Returns the toast ID.

tsx
const id = manager.add({ title: 'Saved', type: 'success' });

#update(id, options)

Modify an existing toast in-place — title, description, type, timeout, or action. This is what powers progress toasts, multi-step flows, and retry patterns.

tsx
manager.update(id, {
  title: 'Upload complete',
  type: 'success',
  timeout: 5000,
});

#close(id?)

Dismiss a specific toast by ID, or dismiss all toasts when called with no arguments.

tsx
manager.close(id);   // close one toast
manager.close();     // close all toasts

#promise(promise, options)

Track a promise with automatic loading, success, and error states. Each state accepts full toast options or a function that receives the resolved value / error.

tsx
manager.promise(
  fetch('/api/save', { method: 'POST' }),
  {
    loading: { title: 'Saving...', description: 'Please wait.' },
    success: (data) => ({ title: 'Saved', description: 'Changes applied.' }),
    error: (err) => ({ title: 'Error', description: err.message }),
  }
);

#subscribe(listener)

Observe toast state changes from external systems. Returns an unsubscribe function.

tsx
const unsubscribe = manager.subscribe(() => {
  console.log('Toasts changed:', manager.toasts);
});

#Toast Options

All methods that create or modify a toast accept these options:

tsx
{
  title: ReactNode,
  description: ReactNode,
  type: 'success' | 'error' | 'warning' | 'loading' | string,
  timeout: number,           // ms — use 0 to disable auto-dismiss
  priority: 'low' | 'high',
  actionProps: ButtonHTMLAttributes,
  data: Record<string, unknown>,  // custom data for renderToast
  onClose: () => void,
  onRemove: () => void,
}

#Global Toast Manager

The CLI creates lib/toast.ts with a shared manager instance. Connect it to your React tree once, then use it from anywhere.

Create the shared instance:

tsx
// lib/toast.ts — created by the CLI
import { createToastManager } from '@/components/ui/toast';
export const toastManager = createToastManager();
tsx
// providers.tsx
import { Toast } from '@/components/ui/toast';
import { toastManager } from '@/lib/toast';
 
export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <Toast toastManager={toastManager}>
      {children}
    </Toast>
  );
}
ts
// api-client.ts
import { toastManager } from '@/lib/toast';
 
export async function apiCall() {
  try {
    const res = await fetch('/api/data');
    if (!res.ok) throw new Error('Request failed');
    return res.json();
  } catch (err) {
    toastManager.add({ title: err.message, type: 'error' });
    throw err;
  }
}

#Custom Rendering

Pass renderToast to ToastList or the Toast convenience wrapper to control toast layout. The default layout is used as a fallback when renderToast is not provided.

tsx
<ToastList
  renderToast={(toast) => (
    <ToastContent>
      <div className="flex items-start gap-3">
        <MyIcon type={toast.type} />
        <div>
          <ToastTitle>{toast.title}</ToastTitle>
          <ToastDescription>{toast.description}</ToastDescription>
        </div>
      </div>
      <ToastClose />
    </ToastContent>
  )}
/>

Custom data is accessible via toast.data. Pass it when creating the toast:

tsx
manager.add({
  title: 'New message',
  data: { avatarUrl: '/img/user.png', sender: 'Alice' },
});

Then read it in your render function:

tsx
// In renderToast:
<img src={toast.data?.avatarUrl} alt={toast.data?.sender} />

#API Reference

PropTypeDefaultDescription
Toast.limitnumber3Maximum number of visible toasts
Toast.timeoutnumber5000Auto-dismiss timeout in milliseconds. Use 0 to prevent auto-dismiss.
Toast.toastManagerToastManagerExternal toast manager created via createToastManager() for global usage
Toast.renderToast(toast: ToastObject) => ReactNodeCustom render function for toasts. Receives the toast object and returns JSX. Falls back to the default layout when not provided.
ToastRoot.variant'default' | 'destructive' | 'success' | 'warning' | 'loading''default'The visual style variant of the toast
ToastRoot.toastToastObjectThe toast data object from the manager (required)
ToastRoot.swipeDirectionArray<'up' | 'down' | 'left' | 'right'>['down', 'right']The directions in which the toast can be swiped to dismiss
ToastList.renderToast(toast: ToastObject) => ReactNodeCustom render function for toasts. Same as Toast.renderToast but applied directly to ToastList.
ToastClose.childrenReactNodeCustom close icon. Renders an XIcon from lucide-react by default.
ToastPositioner.toastToastObjectThe toast data object (required). Positions the toast relative to an anchor element.
ToastPositioner.anchorElement | nullThe element to anchor the toast to.
ToastPositioner.side'top' | 'bottom' | 'left' | 'right' | 'inline-end' | 'inline-start''top'Which side of the anchor to position the toast on.
ToastPositioner.align'start' | 'center' | 'end''center'Alignment along the anchor edge.
ToastPositioner.sideOffsetnumber0Distance in pixels from the anchor.
ToastArrow.classNamestringAdditional classes for the decorative arrow. Styled with directional data-side attributes.
manager.add()(options: { title?, description?, type?, timeout?, actionProps?, data?, priority? }) => stringCreates a toast notification and returns its ID
manager.update()(id: string, updates) => voidUpdates an existing toast's title, description, type, timeout, or actionProps
manager.close()(id?: string) => voidCloses a toast by its ID, or dismisses all toasts when called with no arguments
manager.promise()(promise, { loading, success, error }) => PromiseBinds a promise to a toast that auto-transitions through loading, success, and error states
manager.subscribe()(listener: () => void) => () => voidObserves toast state changes. Returns an unsubscribe function.