Overview
Use Toast for short feedback that appears after an action completes or a background event fires: saves, copies, uploads, syncs, and lightweight errors. A toast confirms something happened. It must not carry information people need later — anything that changes what they should do next belongs on the page, near the content it affects.
Honest UI includes two toast styles driven by one API:
- Toast: a quiet, utilitarian notification built on Base UI. Use it for most product feedback.
- Gooey Toast: an animated notification with morphing transitions (
variant: "gooey"). Use it sparingly, when the notification is part of an expressive product moment such as a celebratory save or upload completion.
Both styles render from the same toastManager and share status types, actions, positions, durations, and promise helpers.
Anatomy
A toast has a title, optional description, an optional status icon, an optional action button, and a dismiss button. The ToastProvider mounts the viewport that renders the stack of active toasts, so it stays installed once near the application root while pages call toastManager.add from anywhere.
The stack collapses behind the frontmost toast: older notifications peek out underneath until you hover or move focus to the viewport, which expands the stack so every toast is reachable.
Behavior
Statuses. Pass type to match the result: success, error, warning, info, or loading. The icon and its color follow automatically. Use promise helpers when an async action moves through loading, success, and error — see the promise example.
Timers. By default a toast auto-dismisses after 5 seconds (gooey after 6). The timer pauses while you point at the stack, while keyboard focus is inside it, and while the browser window is unfocused, then resumes where it left off. Set timeout: 0 (standard) or duration: null (gooey) to keep a toast until dismissed.
Stack limits. The provider keeps at most 3 toasts (limit). When the limit is reached, the oldest toast is removed to make room — one more reason critical information does not belong here.
Swipe. Swiping dismisses a toast. The allowed directions follow the position automatically: edge positions swipe toward their nearest edge, centered positions swipe up (top) or down (bottom).
Choose the standard style when the interface should feel calm. Choose gooey when motion carries meaning for the moment; everything else about the API stays the same.
Accessibility
The viewport is a region with an implicit polite live region: new toasts are announced by screen readers without stealing focus from the current task. For messages that must interrupt — a failed background job, for instance — set priority: "high"; the toast is then additionally mirrored into a visually hidden role="alert" container, which assistive technology announces urgently.
Keyboard users can reach toasts without hunting: pressing F6 anywhere moves focus into the viewport and pauses all timers, Tab reaches each toast's action and dismiss buttons, and Escape closes the focused toast.
Keep titles short and put recovery detail in the description. Because timers pause on hover and focus rather than resetting, reading time extends naturally — but never rely on that for long content.
A toast is supplemental feedback. Errors that affect form content must also appear inline near the field or section they belong to, in dark mode and light, since the toast may expire before anyone acts on it. Messages use theme tokens and wrap rather than truncate; localized descriptions longer than two lines are a signal to shorten them.
Installation
ToastProvider to your app.import { ToastProvider } from "@/components/ui/toast"
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<ToastProvider>
<main>{children}</main>
</ToastProvider>
</body>
</html>
)
}Usage
import { toastManager } from "@/components/ui/toast";toastManager.add({
title: "Event has been created",
description: "Monday, January 3rd at 6:00pm",
});By default, standard toasts appear in the bottom-right corner and gooey toasts in the top-right. Change either per provider:
<ToastProvider position="top-center">{children}</ToastProvider>Allowed values for both: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. A single call can also override the position with its own position option.
Add a status with type:
toastManager.add({
type: "success",
title: "Saved",
description: "Your changes are live.",
});Use variant: "gooey" for the animated style from the same API. It combines with statuses, actions, positions, durations, and promise helpers:
toastManager.add({
variant: "gooey",
type: "success",
title: "Saved",
description: "Your changes are live.",
position: "top-right",
});Don't do this
Critical errors that require action
// Bad
toastManager.add({
type: "error",
title: "Payment failed",
description: "Your card was declined.",
});// Good
<Alert variant="error">
<AlertTitle>Payment failed</AlertTitle>
<AlertDescription>Your card was declined.</AlertDescription>
<AlertAction>
<Button>Update payment method</Button>
</AlertAction>
</Alert>Toasts vanish — after five seconds the decline no longer exists anywhere on screen, yet checkout cannot continue without a response. When the message demands action or blocks progress, put it inline next to the affected content, or use Alert Dialog when work must stop until the person decides.
Auto-dismissing before the message is read
// Bad
toastManager.add({ title: error.message, timeout: 1200 });
toastManager.add({ title: "Syncing…", timeout: 0 });// Good
toastManager.add({ title: "Invitation not sent", type: "error", timeout: 8000 });
toastManager.add({ title: "Sync complete", description: "All records backed up." });A 1.2-second toast is gone before anyone finishes reading it, and errors deserve extra time because people reread them. The inverse mistake is just as real: pinning everything with timeout: 0 floods the three-slot stack and silently evicts older toasts. Give errors roughly eight seconds, let routine confirmations take the default, and reserve sticky toasts for genuinely ongoing states like uploads.
Confirming destruction without undo
// Bad
await deleteFile(id);
toastManager.add({ title: "File deleted" });// Good
await deleteFile(id);
toastManager.add({
title: "File deleted",
description: "Moved to trash.",
actionProps: {
children: "Undo",
onClick: () => restoreFile(id),
},
});A fire-and-forget "Deleted!" tells people the loss is permanent the instant it happens. If the operation can be soft-deleted or reversed within a window, surface that escape hatch directly on the toast so recovery takes one click instead of a search through documentation.
Examples
With Status
Success, error, info, warning, and loading icons driven by type.
Loading
An in-progress toast with the spinner icon.
With Action
An Undo button on the toast itself; activating it closes the toast and reports the reversal.
Promise
One call drives loading, success, and error states from a single promise.
Varying Heights
Stacking behavior when descriptions differ in length; the collapsed stack expands on hover or focus.
Gooey States
The animated style across success, error, warning, info, and action states.
Gooey Promise
Promise-driven state transitions in the animated style.
Gooey With Action Button
A restorable delete with the action button rendered inside the morphing pill.
Gooey Position
The animated style placed from any screen edge.
API reference
ToastProvider
Installs the viewport once near the application root.
toastManager
Toast options
Gooey-only options: duration (number or null; null never auto-dismisses, default 6000), icon, styles, fill, roundness, autopilot (boolean or { expand?, collapse? } timing in ms), button ({ title, onClick }), and state to force a gooey state directly.
Keep the provider mounted once near the application root. Toasts are supplemental feedback: persist errors or results people may need after the timeout elsewhere, and never use a toast as the only confirmation of a destructive action.