Toast

Dictionary-driven notifications built on Sonner

Installation

pnpm add @wandercom/design-system-web

Integration

Add the Toaster component to your application root layout or provider:

import { Toaster } from '@wandercom/design-system-web/ui/toast';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <>
      {children}
      <Toaster />
    </>
  );
}

Usage

Import the toast object to trigger toasts. The label and description props accept both dictionary keys and direct strings:

import { toast } from '@wandercom/design-system-web/ui/toast';

export function Example() {
  return (
    <>
      <button onClick={() => toast.error({ label: 'ErrorGeneral', description: 'ErrorGeneral' })}>
        Show error toast
      </button>

      <button onClick={() => toast.success({ label: 'Success!', description: 'Your changes have been saved.' })}>
        Show success toast
      </button>
    </>
  );
}

Examples

Loading example...

Dictionary keys vs direct strings

The label and description props accept both dictionary keys and direct strings. When a key exists in the dictionary, the resolved copy is displayed. Otherwise, the string is used as-is.

// Dictionary keys (resolved from the toast dictionary)
toast.success({
  label: 'CopiedToClipboard',
  args: { value: 'API key' },
});

// Direct strings
toast.success({
  label: 'Copied to clipboard!',
  description: 'Your API key has been copied.',
});

// Mixing both
toast.error({
  label: 'ErrorGeneral',
  description: 'Please try again in a few moments.',
});

With actions

Add action buttons to toasts for user interaction. When dismissible is true (the default), clicking an action also dismisses the toast.

toast.default({
  label: 'ErrorActionFailed',
  description: 'ErrorActionFailed',
  actions: [
    {
      label: 'Try again',
      onClick: () => {
        console.log('Clicked try again');
      },
    },
    {
      label: 'Dismiss',
      onClick: () => {
        console.log('Dismissed');
      },
    },
  ],
});

Inline layout

Set inline to render the label and description side-by-side instead of stacked:

toast.info({
  label: 'Theme',
  description: 'System preference',
  inline: true,
});

Duration and dismissible

Control how long toasts are displayed and whether they can be dismissed:

toast.success({
  label: 'CopiedToClipboard',
  duration: 3000,
});

toast.info({
  label: 'ChangedTheme',
  args: { value: 'system' },
  dismissible: false,
});

Promise toast

Track promise-based operations with automatic loading, success, and error states:

toast.promise<{ name: string }>(
  () => fetch('/api/booking').then((r) => r.json()),
  {
    loading: 'Processing...',
    success: (data) => `${data.name} confirmed successfully`,
    error: 'Failed to process booking',
  },
);

Each phase (loading, success, error) accepts a dictionary key, a direct string, or a full props object:

toast.promise<{ name: string }>(
  () => fetch('/api/booking').then((r) => r.json()),
  {
    loading: { label: 'Processing...', description: 'Please wait' },
    success: (data) => ({
      label: 'Booking confirmed',
      description: `${data.name} is all set`,
    }),
    error: { label: 'ErrorGeneral', description: 'ErrorGeneral' },
  },
);

The promise method shows a loading toast while the promise is pending, then automatically transitions to a success or error toast based on the result. The success and error options also accept a callback that receives the resolved data or error, respectively. The returned promise can be chained further.

Custom dictionaries

Use createToast to build a toast instance backed by a custom dictionary. This keeps copy centralized and type-safe across your application.

import { createToast } from '@wandercom/design-system-web/ui/toast';
import { createToastDictionary } from '@wandercom/design-system-shared/dictionaries/toasts';

const APP_TOASTS = createToastDictionary({
  BookingConfirmed: {
    label: 'Booking confirmed',
    description: ({ value }) => `Welcome, ${value}!`,
  },
} as const);

export const appToast = createToast(APP_TOASTS);

// Usage -- dictionary keys are fully typed
appToast.success({ label: 'BookingConfirmed', args: { value: 'John' } });

The shared dictionary keys (ErrorGeneral, CopiedToClipboard, etc.) are automatically included via createToastDictionary.

Toast methods

toast.default()

Notification without a preset icon. Accepts a custom icon prop.

toast.success()

Success notification with a green checkmark icon.

toast.error()

Error notification with a red exclamation icon.

toast.info()

Informational notification with an info icon.

toast.loading()

Loading notification with an animated spinner.

toast.promise()

Promise-based notification that transitions through loading, success, and error states automatically.

Props

All toast methods accept the following props (except toast.promise(), documented above):

label?:

string
Primary message text. Resolved from the dictionary if a matching key exists, otherwise used directly.

description?:

string
Secondary text displayed below the label. Resolved from the dictionary if a matching key exists, otherwise used directly.

args?:

ToastArgs
Dynamic arguments for interpolating dictionary messages. Supports count (number), date (Date), and value (string | boolean | number).

actions?:

{ label: string; onClick: () => void }[]
Action buttons displayed below the message. Clicking an action also dismisses the toast when dismissible is true.

icon?:

ReactElement
Custom icon element. Only available on toast.default() since other methods provide their own icon.

inline?:

boolean
Renders label and description side-by-side instead of stacked. Defaults to false.

dismissible?:

boolean
Whether the toast shows a close button and can be manually dismissed. Defaults to true.

duration?:

number
How long the toast is displayed in milliseconds.

Toaster props

The Toaster component accepts all Sonner ToasterProps. Defaults are applied for position (bottom-right) and toast width styling.

theme?:

'light' | 'dark' | 'system'
Theme for the toaster. Defaults to 'system'.

position?:

'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
Position of the toaster on screen. Defaults to 'bottom-right'.

Accessibility

Toast notifications are rendered by Sonner, which manages an aria-live region so screen readers announce new toasts automatically.

  • Each toast is rendered inside an accessible container with appropriate aria-live politeness
  • Dismissible toasts include a close button reachable via keyboard
  • Action buttons within toasts are focusable and keyboard-operable
  • Toasts do not trap focus, allowing users to continue interacting with the page
Toast