Toast
Dictionary-driven notifications built on Sonner
pnpm add @wandercom/design-system-web
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 />
</>
);
}Toasts remain interactive above modal layers. Touch, pointer, click, and focus events are consumed within the toaster, so dismissing a notification or using an action does not activate controls or dismiss overlays underneath.
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>
</>
);
}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.',
});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');
},
},
],
});Set inline to render the label and description side-by-side instead of stacked:
toast.info({
label: 'Theme',
description: 'System preference',
inline: true,
});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,
});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.
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.
Notification without a preset icon. Accepts a custom icon prop.
Success notification with a green checkmark icon.
Error notification with a red exclamation icon.
Informational notification with an info icon.
Loading notification with an animated spinner.
Promise-based notification that transitions through loading, success, and error states automatically.
All toast methods accept the following props (except toast.promise(), documented above):
label?:
description?:
args?:
actions?:
icon?:
inline?:
dismissible?:
duration?:
The Toaster component accepts all Sonner ToasterProps. Defaults are applied for position (bottom-right) and toast width styling.
theme?:
position?:
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-livepoliteness - 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