Toast
Display temporary notifications and messages to users with automatic dismissal and customizable placement
Usage
import { Toast, toast } from '@lenso/ui';"use client";
// Adapted from HeroUI v3.2.6 toast-default (Apache-2.0).import { Persons } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function InvitationQueue() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "You have been invited to join a team", description: "Bob sent you an invitation to join HeroUI team", type: "default", data: { indicator: <Persons />, actionStyle: "tertiary" }, actionProps: { children: "Dismiss", onClick: () => manager.close(id) }, }); }} > Show toast </Button> </div> <Notifications /> </> );}
export function Default() { return ( <Toast.Provider> <InvitationQueue /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Anatomy
<Toast.Provider> <Toast> <Toast.Indicator /> <Toast.Content> <Toast.Title /> <Toast.Description /> </Toast.Content> <Toast.ActionButton /> <Toast.CloseButton /> </Toast></Toast.Provider>Examples
Variants
"use client";
// Adapted from HeroUI v3.2.6 variants, Apache-2.0.import { HardDrive, Persons } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function Messages() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have been invited to join a team", description: "Bob sent you an invitation to join HeroUI team", type: "default", data: { indicator: <Persons />, actionStyle: "tertiary" }, actionProps: { children: "Dismiss", onClick: () => manager.close(id) }, }); }} > Default toast </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "You have 2 credits left", description: "Get a paid plan for more credits", type: "accent", actionProps: { children: "Upgrade", onClick: () => manager.close(id) }, }); }} > Accent toast </Button> <Button xstyle={styles.successText} size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have upgraded your plan", description: "You can continue using HeroUI Chat", type: "success", data: { actionStyle: "success" }, actionProps: { children: "Billing", onClick: () => manager.close(id) }, }); }} > Success toast </Button> <Button xstyle={styles.warningText} size="sm" variant="tertiary" onClick={() => { const id = manager.add({ title: "You have no credits left", description: "Upgrade to a paid plan to continue", type: "warning", data: { actionStyle: "warning" }, actionProps: { children: "Upgrade", onClick: () => manager.close(id) }, }); }} > Warning toast </Button> <Button size="sm" variant="danger-soft" onClick={() => { const id = manager.add({ title: "Storage is full", description: "Remove files to release space. Adding more text to demonstrate longer content display", type: "danger", data: { indicator: <HardDrive />, actionStyle: "danger" }, actionProps: { children: "Remove", onClick: () => manager.close(id) }, }); }} > Danger toast </Button> </div> </div> <Notifications /> </> );}
export function Variants() { return ( <Toast.Provider> <Messages /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Placements
"use client";
// Adapted from HeroUI v3.2.6 placements, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const placements = ["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"] as const;const placementQueues = { "top-start": Toast.createToastManager(), top: Toast.createToastManager(), "top-end": Toast.createToastManager(), "bottom-start": Toast.createToastManager(), bottom: Toast.createToastManager(), "bottom-end": Toast.createToastManager(),};
export function Placements() { return ( <div {...stylex.props(styles.placements)}> {placements.map((placement) => ( <Toast.Provider key={placement} toastManager={placementQueues[placement]} limit={3}> <Notifications placement={placement} /> </Toast.Provider> ))} <div {...stylex.props(styles.placementButtons)}> {placements.map((placement) => ( <Button key={placement} size="sm" variant="secondary" onClick={() => placementQueues[placement].add({ description: "Event has been created", title: "Event created", type: "default", }) } > {placement.replace("-", " ")} </Button> ))} </div> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Expanded Stack
"use client";
// Adapted from HeroUI v3.2.6 expanded, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const queue = Toast.createToastManager();
export function Expanded() { return ( <div {...stylex.props(styles.frame)}> <Toast.Provider toastManager={queue}> <Notifications expanded aria-label="Expanded notifications" /> </Toast.Provider> <Button size="sm" variant="secondary" onClick={() => { queue.add({ title: "Simple message", type: "default" }); setTimeout(() => queue.add({ title: "Operation completed", type: "success" }), 400); setTimeout(() => queue.add({ title: "New update available", type: "accent" }), 800); }} > Show 3 toasts </Button> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Simple Toasts
"use client";
// Adapted from HeroUI v3.2.6 simple, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
function Messages() { const manager = Toast.useToastManager(); return ( <> <div {...stylex.props(styles.frame)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Simple message", type: "default" })} > Default </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Operation completed", type: "success" })} > Success </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "New update available", type: "accent" })} > Info </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Please check your settings", type: "warning" })} > Warning </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Something went wrong", type: "danger" })} > Error </Button> </div> </div> <Notifications /> </> );}
export function Simple() { return ( <Toast.Provider> <Messages /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Custom Indicators
"use client";
// Adapted from HeroUI v3.2.6 custom-indicator, Apache-2.0.import { Star } from "@gravity-ui/icons";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles, type ToastData } from "./_shared";
function Message() { const manager = Toast.useToastManager<ToastData>(); return ( <> <div {...stylex.props(styles.frame)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Custom icon indicator", data: { indicator: <Star /> } }) } > Custom indicator </Button> </div> <Notifications /> </> );}
export function CustomIndicator() { return ( <Toast.Provider> <Message /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Custom Toast Rendering
"use client";
// Adapted from HeroUI v3.2.6 custom-toast, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, CloseIcon, Toast } from "@lenso/ui";import { styles } from "./_shared";
function CustomLayout() { const manager = Toast.useToastManager(); return ( <> <Toast.Portal> <Toast.Viewport placement="bottom"> {manager.toasts.map((item) => ( <Toast key={item.id} toast={item} xstyle={styles.customRoot}> <Toast.Content> <div {...stylex.props(styles.customRow)}> <Toast.Indicator xstyle={styles.accent} /> <div {...stylex.props(styles.customText)}> {item.title && <Toast.Title xstyle={styles.accent} />} {item.description && <Toast.Description />} </div> </div> </Toast.Content> <Toast.Close xstyle={styles.customClose} aria-label="Close notification"> <CloseIcon {...stylex.props(styles.customCloseIcon)} /> </Toast.Close> </Toast> ))} </Toast.Viewport> </Toast.Portal> <Button size="sm" variant="secondary" onClick={() => manager.add({ description: "This uses a custom render function", title: "Custom layout toast", type: "default", }) } > Custom toast </Button> </> );}
export function CustomToast() { return ( <div {...stylex.props(styles.frame)}> <Toast.Provider> <CustomLayout /> </Toast.Provider> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Promise & Loading
"use client";
// Adapted from HeroUI v3.2.6 promise, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
const uploadFile = (): Promise<{ filename: string; size: number }> => new Promise((resolve) => { setTimeout(() => resolve({ filename: "document.pdf", size: 1024 }), 2000); });const createEvent = (): Promise<never> => new Promise((_, reject) => { setTimeout(() => reject(new Error("Network error. Please try again.")), 2000); });const saveData = (): Promise<{ count: number }> => new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) resolve({ count: 42 }); else reject(new Error("Failed to save data")); }, 2000); });const fetchUser = (): Promise<{ name: string; email: string }> => new Promise((resolve) => { setTimeout(() => resolve({ email: "[email protected]", name: "John Doe" }), 2000); });
function Workflows() { const manager = Toast.useToastManager(); return ( <> <div {...stylex.props(styles.frame, styles.promise)}> <div {...stylex.props(styles.section)}> <div {...stylex.props(styles.center)}> <h3 {...stylex.props(styles.heading)}>Using toast.promise()</h3> <p {...stylex.props(styles.help)}> Automatically handles loading, success, and error states </p> </div> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(uploadFile(), { error: { title: "Failed to upload file" }, loading: { title: "Uploading file..." }, success: (data) => ({ title: `File ${data.filename} uploaded (${data.size}KB)`, }), }) .catch(() => {}); }} > Upload file </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(createEvent(), { error: (error: Error) => ({ title: error.message }), loading: { title: "Creating event..." }, success: { title: "Event created" }, }) .catch(() => {}); }} > Create event (error) </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(saveData(), { error: (error: Error) => ({ title: error.message }), loading: { title: "Saving changes..." }, success: (data) => ({ title: `Saved ${data.count} items` }), }) .catch(() => {}); }} > Save data (random) </Button> <Button size="sm" variant="secondary" onClick={() => { void manager .promise(fetchUser(), { error: { title: "Failed to fetch user" }, loading: { title: "Loading user..." }, success: (data) => ({ title: `Welcome back, ${data.name}!` }), }) .catch(() => {}); }} > Fetch user </Button> </div> </div> <div {...stylex.props(styles.section)}> <div {...stylex.props(styles.center)}> <h3 {...stylex.props(styles.heading)}>Manual Loading State</h3> <p {...stylex.props(styles.help)}>Manually control loading state with isLoading prop</p> </div> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Uploading file...", description: "Please wait while we upload your file", type: "loading", timeout: 0, }); setTimeout( () => manager.update(id, { title: "File uploaded", description: "Your file has been uploaded successfully", type: "success", timeout: 5000, }), 3000, ); }} > Upload with loading </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Processing payment...", type: "loading", timeout: 0, }); setTimeout( () => manager.update(id, { title: "Payment processed", description: "Your payment has been processed successfully", type: "success", timeout: 5000, }), 2500, ); }} > Payment processing </Button> <Button size="sm" variant="secondary" onClick={() => { const id = manager.add({ title: "Saving changes...", type: "loading", timeout: 0 }); setTimeout( () => manager.update(id, { title: "Failed to save", description: "Please try again", type: "danger", timeout: 5000, }), 2000, ); }} > Loading to error </Button> </div> </div> </div> <Notifications /> </> );}
export function PromiseDemo() { return ( <Toast.Provider> <Workflows /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Callbacks
"use client";
// Adapted from HeroUI v3.2.6 callbacks, Apache-2.0.import { useRef, useState } from "react";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
function History() { const manager = Toast.useToastManager(); const [closedHistory, setClosedHistory] = useState< Array<{ id: number; message: string; time: string }> >([]); const nextId = useRef(0); const addToHistory = (message: string) => { const time = new Date().toLocaleTimeString(); const id = nextId.current++; setClosedHistory((previous) => [{ id, message, time }, ...previous].slice(0, 5)); }; return ( <> <div {...stylex.props(styles.frame, styles.large)}> <div {...stylex.props(styles.buttons)}> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "File saved", onClose: () => addToHistory("File saved (closed after 3 seconds)"), timeout: 3000, }) } > Custom timeout (3s) </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Changes saved", onClose: () => addToHistory("Changes saved (closed after 10 seconds)"), timeout: 10000, }) } > Custom timeout (10s) </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Event created", type: "success", onClose: () => addToHistory("Event created (closed after default timeout)"), }) } > With onClose callback </Button> <Button size="sm" variant="secondary" onClick={() => manager.add({ title: "Important notification", description: "This toast will stay until dismissed", onClose: () => addToHistory("Important notification (manually closed)"), timeout: 0, }) } > Persistent toast </Button> </div> <div {...stylex.props(styles.history)}> <div {...stylex.props(styles.historyHeading)}> <h3 {...stylex.props(styles.heading)}>Closed History</h3> {closedHistory.length > 0 && ( <Button xstyle={styles.clear} size="sm" variant="tertiary" onClick={() => setClosedHistory([])} > Clear </Button> )} </div> <div {...stylex.props(styles.historyPanel)}> {closedHistory.length === 0 ? ( <p {...stylex.props(styles.empty)}>No toasts closed yet. Try closing one above!</p> ) : ( closedHistory.map((item, index) => ( <div key={item.id} {...stylex.props(styles.historyItem, styles.historyDelay(index * 50))} > <div {...stylex.props(styles.historyText)}> <span {...stylex.props(styles.medium)}>{item.message}</span> <span {...stylex.props(styles.time)}>({item.time})</span> </div> <div {...stylex.props(styles.check)}> <svg {...stylex.props(styles.checkIcon)} fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24" aria-hidden="true" > <path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round" /> </svg> </div> </div> )) )} </div> </div> </div> <Notifications /> </> );}
export function Callbacks() { return ( <Toast.Provider> <History /> </Toast.Provider> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Custom Queues
"use client";
// Adapted from HeroUI v3.2.6 custom-queue, Apache-2.0.import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { Notifications, styles } from "./_shared";
export function CustomQueue() { const [notificationQueue] = useState(() => Toast.createToastManager()); const [errorQueue] = useState(() => Toast.createToastManager()); const [successQueue] = useState(() => Toast.createToastManager()); return ( <div {...stylex.props(styles.queues)}> <Toast.Provider toastManager={notificationQueue} limit={2}> <Notifications placement="bottom" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button size="sm" variant="secondary" onClick={() => notificationQueue.add({ description: "You have a new message", title: "New notification", type: "default", }) } > Add notification (max 2) </Button> </div> <Toast.Provider toastManager={errorQueue} limit={3}> <Notifications placement="bottom-start" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button size="sm" variant="danger-soft" onClick={() => errorQueue.add({ description: "Failed to save changes", title: "Error occurred", type: "danger", }) } > Add error (max 3) </Button> </div> <Toast.Provider toastManager={successQueue} limit={1}> <Notifications placement="bottom-end" /> </Toast.Provider> <div {...stylex.props(styles.queueButtons)}> <Button xstyle={styles.successText} size="sm" variant="secondary" onClick={() => successQueue.add({ description: `Operation ${Date.now()}`, title: "Success!", type: "success", }) } > Add success (max 1) </Button> </div> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Setup
Render the provider in the root of your app.
import { Toast, Button, toast } from '@lenso/ui';
function App() { return ( <div> <Toast.Provider /> <Button onPress={() => toast("Simple message")}> Show toast </Button> </div> );}Customization
Tailwind CSS
"use client";
// Adapted from HeroUI v3.2.6 custom-styles, Apache-2.0.import * as stylex from "@stylexjs/stylex";import { Button, Toast } from "@lenso/ui";import { styles } from "./_shared";
function StyledNotifications() { const manager = Toast.useToastManager(); return ( <> <Toast.Portal> <Toast.Viewport placement="bottom"> {manager.toasts.map((item) => ( <Toast key={item.id} toast={item} xstyle={styles.styledRoot}> <Toast.Content> <div {...stylex.props(styles.styledRow)}> <Toast.Indicator xstyle={styles.neutral} /> <div {...stylex.props(styles.styledText)}> {item.title && <Toast.Title xstyle={styles.styledTitle} />} {item.description && <Toast.Description xstyle={styles.styledDescription} />} </div> </div> </Toast.Content> </Toast> ))} </Toast.Viewport> </Toast.Portal> <Button size="sm" variant="secondary" onClick={() => manager.add({ description: "Draft synced", title: "Saved", type: "default" }) } > Show toast </Button> </> );}
export function CustomStyles() { return ( <div {...stylex.props(styles.customFrame)}> <Toast.Provider> <StyledNotifications /> </Toast.Provider> </div> );}Local adaptation source above. Derived from HeroUI v3.2.6 source.
Global CSS
To customize the Toast component classes, you can use the @layer components directive.
Learn more.
@layer components { .toast { @apply rounded-xl shadow-lg; }
.toast__content { @apply gap-2; }}Styling Reference
HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.
CSS Classes
The Toast component uses these CSS classes (View source styles):
Base Classes [!toc]
.toast- Base toast container.toast-region- Toast region container.toast__content- Content wrapper for title and description.toast__indicator- Icon/indicator container.toast__title- Toast title text.toast__description- Toast description text.toast__action- Action button container.toast__close-button- Close button container
Variant Classes [!toc]
.toast--default- Default gray variant.toast--accent- Accent blue variant.toast--success- Success green variant.toast--warning- Warning yellow/orange variant.toast--danger- Danger red variant
Interactive States
The component supports various states:
- Frontmost:
[data-frontmost]- Applied to the topmost visible toast - Index:
[data-index]- Applied based on toast position in stack - Placement:
[data-placement="*"]- Applied based on toast region placement - Entering:
[data-entering]- Applied on first paint of a new toast - Exiting:
[data-exiting]- Applied while a closing toast plays its exit transition - Expanded:
[data-expanded]- Applied to every toast while the stack is expanded - Hidden:
[data-hidden]- Applied to toasts beyondmaxVisibleToasts
API Reference
Toast.Provider
| Prop | Type | Default | Description |
|---|---|---|---|
placement | "top start" | "top" | "top end" | "bottom start" | "bottom" | "bottom end" | "bottom" | Placement of the toast region |
gap | number | 12 | The gap between toasts in pixels |
isExpanded | boolean | false | Force the stack into its expanded layout (does not pause timers) |
maxVisibleToasts | number | 3 | Maximum number of toasts to display at once |
hotkey | string[] | ["altKey", "KeyT"] | Hotkey that moves focus to the toast region and expands the stack. Modifiers match KeyboardEvent boolean properties (e.g. "altKey"), other keys match event.code (e.g. "KeyT"). Modifiers you leave out must be up, so Alt+T does not also fire on Ctrl+Alt+T. Pass [] to disable |
scaleFactor | number | 0.05 | Scale factor for stacked toasts (0-1) |
width | number | string | 460 | Width of the toast in pixels or CSS value |
queue | ToastQueue<T> | - | Custom toast queue instance |
children | ReactNode | ((props: {toast: QueuedToast<T>}) => ReactNode) | - | Custom render function or children |
className | string | - | Additional CSS classes |
Toast
| Prop | Type | Default | Description |
|---|---|---|---|
toast | QueuedToast<T> | - | Toast data from queue (required) |
variant | "default" | "accent" | "success" | "warning" | "danger" | "default" | Visual variant of the toast |
placement | ToastVariants["placement"] | - | Placement (inherited from Provider) |
scaleFactor | number | - | Scale factor (inherited from Provider) |
className | string | - | Additional CSS classes |
children | ReactNode | - | Toast content (ToastContent, ToastIndicator, etc.) |
Toast.Content
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Content (typically ToastTitle and ToastDescription) |
className | string | - | Additional CSS classes |
Toast.Indicator
| Prop | Type | Default | Description |
|---|---|---|---|
variant | ToastVariants["variant"] | - | Variant for default icon |
children | ReactNode | - | Custom indicator icon (defaults to variant icon) |
className | string | - | Additional CSS classes |
Toast.Title
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Title text |
className | string | - | Additional CSS classes |
Toast.Description
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Description text |
className | string | - | Additional CSS classes |
Toast.ActionButton
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Action button content |
className | string | - | Additional CSS classes |
All Button props | - | - | Accepts all Button component props |
Toast.CloseButton
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional CSS classes |
All CloseButton props | - | - | Accepts all CloseButton component props |
ToastQueue
A ToastQueue manages the state for a <Toast.Provider>. The state is stored outside React so you can trigger toasts from anywhere in your application.
Constructor Options
| Option | Type | Default | Description |
|---|---|---|---|
exitDuration | number | 300 | How long a closing toast stays mounted for its exit animation (0 removes immediately) |
maxVisibleToasts | number | 3 | Maximum number of toasts to display at once (visual only) |
wrapUpdate | (fn: () => void) => void | - | Function to wrap state updates (e.g. document.startViewTransition); by default updates are applied directly and animated with CSS transitions |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
add | (content: T, options?: ToastOptions) | string | Add a toast to the queue, returns toast key |
update | (key: string, content: T, options?: {timeout?: number; onClose?: () => void}) | boolean | Update a toast in place, preserving its stack position; timeout restarts the countdown (0 keeps it open), omit it to keep the current countdown. Returns false if the toast no longer exists |
close | (key: string) | void | Close a toast by its key |
pauseAll | () | void | Pause all toast timers |
resumeAll | () | void | Resume all toast timers |
clear | () | void | Close all toasts (each toast animates out and fires its onClose) |
subscribe | (fn: () => void) | () => void | Subscribe to queue changes, returns unsubscribe function |
toast Function
The default toast function provides convenient methods for showing toasts:
import { toast } from '@lenso/ui';
// Basic toast (auto-dismisses after 4 seconds by default)toast("Event has been created");
// Variant methods (also auto-dismiss after 4 seconds by default)toast.success("File saved");toast.info("New update available");toast.warning("Please check your settings");toast.danger("Something went wrong");
// With options. The returned id lets the action close its own toast.const eventId = toast("Event has been created", { description: "Your event has been scheduled for tomorrow", variant: "default", timeout: 5000, // Custom timeout: 5 seconds onClose: () => console.log("Closed"), actionProps: { children: "View", onPress: () => toast.close(eventId), }, indicator: <CustomIcon />,});
// Update an existing toast in place (keeps its position in the stack).// Options you omit are inherited, so pass `timeout` to start a countdown// on a toast that was created persistent.const id = toast("Saving…", { timeout: 0 });toast.update(id, "Saved", { variant: "success", timeout: 4000 });
// Promise support (automatically shows loading spinner). The loading toast// updates in place when the promise settles — same toast, same stack// position; the auto-dismiss countdown starts at that point.toast.promise( uploadFile(), { loading: "Uploading file...", success: (data) => `File ${data.filename} uploaded`, error: "Failed to upload file", });
// Manual loading state (persistent toast - no auto-dismiss)const loadingId = toast("Creating event...", { isLoading: true, timeout: 0, // Persistent toast that doesn't auto-dismiss});
// Later, update in place and start the auto-dismiss countdowntoast.update(loadingId, "Event created", { variant: "success", timeout: 4000 });
// Queue methodstoast.close(key);toast.clear();toast.pauseAll();toast.resumeAll();toast Options
| Option | Type | Default | Description |
|---|---|---|---|
title | ReactNode | - | Toast title (first parameter for variant methods) |
description | ReactNode | - | Optional description text |
variant | "default" | "accent" | "success" | "warning" | "danger" | "default" | Visual variant |
indicator | ReactNode | - | Custom indicator icon (null to hide) |
actionProps | ButtonProps | - | Props for action button |
isLoading | boolean | false | Show loading spinner instead of indicator |
timeout | number | 4000 | Auto-dismiss timeout in milliseconds. Defaults to 4000ms (4 seconds). Set to 0 for persistent toasts that don't auto-dismiss |
onClose | () => void | - | Called when the toast is dismissed, as its exit animation starts |
toast.promise Options
| Option | Type | Default | Description |
|---|---|---|---|
loading | ReactNode | - | Message shown while promise is pending |
success | ReactNode | ((data: T) => ReactNode) | - | Message shown on success (can be function) |
error | ReactNode | ((error: Error) => ReactNode) | - | Message shown on error (can be function) |
Accessibility
Each toast implements the WAI-ARIA alertdialog pattern, grouped inside a labeled landmark region:
- Landmark region: Toasts render in a labeled landmark reachable with
F6;Alt+Tmoves focus straight to it. The hotkey is configurable via thehotkeyprop and can be disabled by passing an empty array, and the region label can be overridden witharia-label - Keyboard:
Tabreaches the controls of visible toasts,Escapecollapses the expanded stack, and dismissing a focused toast moves focus to the nearest remaining toast - Screen readers: Each toast uses
role="alertdialog"with its title and description linked; new toasts are announced automatically - Timers: Auto-dismiss pauses while the stack is hovered or focused, and while the page is in a background tab