Toast
Brief, non-blocking notifications with a sonner-style API - toast.success(), toast.promise() and friends.
Installation
npx shadcn@latest add @fujin/toastSetup
Render <Toaster /> once, in your root layout. Every toast() call in the
app shows up there.
import { Toaster } from "@/components/ui/toast"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{children}
<Toaster />
</body>
</html>
)
}Toaster is a client component, so it can go straight into a Server Component
layout. If a second Toaster is mounted by mistake, only the first one shows
toasts (and you get a console warning in development).
Usage
import { toast } from "@/components/ui/toast"toast("Event created")
toast.success("Changes saved", { description: "Your profile is up to date." })
toast.error("Payment failed", { description: "The card was declined." })
toast.warning("Storage almost full")
toast.info("A new version is available")toast is a plain function. Call it from event handlers, effects, or modules
outside React (a fetch wrapper, a store). Toasts fired before the Toaster
has mounted - say, in a page's first effect - are queued and shown once it
does.
Promise
toast.promise(saveInvoice(), {
loading: "Saving invoice...",
success: (invoice) => `Invoice ${invoice.number} saved`,
error: (error) => ({
title: "Could not save the invoice",
description: String(error),
}),
})Shows a loading toast (which never times out), then swaps it for a success or
error toast in place. It returns the original promise, so you can still
await it; a rejection is reported by the toast and never left unhandled.
Action
toast("Message archived", {
action: { label: "Undo", onClick: () => restore(id) },
})The toast closes after the action runs; call event.preventDefault() in
onClick to keep it open. Toasts with an action stay up for 10 seconds by
default instead of 5.
Updating and dismissing
const id = toast.loading("Uploading 3 files...")
// later
toast.success("Upload complete", { id }) // replaces the loading toast
toast.dismiss(id) // or toast.dismiss() to close allAccessibility
- The toast stack is a landmark region named "Notifications" and a polite
live region, so new toasts are announced without moving focus.
toast.errorusespriority: "high", which announces assertively. - Hovering or focusing the stack pauses every timer. F6 moves focus into the stack.
- Toasts can be swiped away, but each one also has a 24px close button, so swiping is never required.
- Only the
titleanddescriptionstrings are announced for high-priority toasts. Pass text, not rich markup, when the message matters. - Do not put the only way to do something in a toast. It disappears, and it is easy to miss with a screen magnifier.
What's different from shadcn/ui
shadcn/ui wraps sonner. Fujin wraps Base UI's Toast behind the same call
shape (toast(), toast.success(), toast.promise(), toast.dismiss()), so
there is no extra dependency. Two differences from sonner:
toast.promisereturns the original promise rather than an object withunwrap().- Rich colors are replaced by a coloured icon per type; the text keeps normal contrast in every theme.
API Reference
Prop
Type
See Base UI's Toast for the underlying manager and parts.