"use client"
import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"
export function ToastDemo() {
function showToast() {
const id = toast.add({
title: "Event created",
description: "Sunday, December 3 at 9:00 AM",
actionProps: {
children: "Undo",
onClick() {
toast.close(id)
},
},
})
}
return (
<Button variant="outline" onClick={showToast}>
Show Toast
</Button>
)
}Installation
npx shadcn@latest add @fulldev/toast
Add the Toaster to the root layout:
import { Toaster } from "@/components/ui/toast"
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<main>{children}</main>
<Toaster />
</body>
</html>
)
}
Usage
import { toast } from "@/components/ui/toast"
toast.add({
title: "Event created",
description: "Sunday, December 3 at 9:00 AM",
})
Types
Set the type option to render a status icon. The built-in renderer recognizes success, info, warning, error, and loading.
"use client"
import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"
export function ToastTypes() {
return (
<div className="flex flex-wrap gap-2">
<Button
variant="outline"
onClick={() => toast.add({ description: "Event has been created." })}
>
Default
</Button>
<Button
variant="outline"
onClick={() =>
toast.add({
type: "success",
description: "Event has been created.",
})
}
>
Success
</Button>
<Button
variant="outline"
onClick={() =>
toast.add({
type: "info",
description: "Arrive 10 minutes before the event.",
})
}
>
Info
</Button>
<Button
variant="outline"
onClick={() =>
toast.add({
type: "warning",
description: "The event cannot start before 8:00 AM.",
})
}
>
Warning
</Button>
<Button
variant="outline"
onClick={() =>
toast.add({
type: "error",
description: "The event could not be created.",
priority: "high",
})
}
>
Error
</Button>
</div>
)
}Action
Pass button props with actionProps to render an action.
const id = toast.add({
title: "Event created",
actionProps: {
children: "Undo",
onClick() {
toast.close(id)
},
},
})
Promise
Use toast.promise to update one toast as an asynchronous task moves through loading, success, and error states.
"use client"
import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"
export function ToastPromise() {
function showToast() {
toast.promise(
new Promise<{ name: string }>((resolve) => {
window.setTimeout(() => resolve({ name: "Event" }), 2000)
}),
{
loading: "Creating event…",
success: (data) => `${data.name} created.`,
error: "Could not create event.",
}
)
}
return (
<Button variant="outline" onClick={showToast}>
Create Event
</Button>
)
}Custom layout
createToastManager, ToastProvider, ToastViewport and the Toast parts build a second toast stack with its own layout.
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
createToastManager,
Toast,
toast,
ToastAction,
ToastClose,
ToastContent,
ToastDescription,
Toaster,
ToastPortal,
ToastProvider,
ToastTitle,
ToastViewport,
useToastManager,
} from "@/components/ui/toast"
// A second manager with its own provider and viewport, for the custom layout.
const customToast = createToastManager()
function CustomToastList() {
const { toasts } = useToastManager()
return toasts.map((item) => (
<Toast key={item.id} toast={item}>
<ToastContent className="items-start">
<div className="flex min-w-0 flex-1 flex-col gap-1">
<ToastTitle />
<ToastDescription />
<div className="mt-2 flex gap-2">
<ToastAction />
<ToastClose render={<Button variant="ghost" size="sm" />}>
Dismiss
</ToastClose>
</div>
</div>
</ToastContent>
</Toast>
))
}
function CustomToastButton() {
const manager = useToastManager()
return (
<Button
variant="outline"
onClick={() =>
manager.add({
title: "Invitation sent",
description: "Ada can now edit this site.",
timeout: 0,
actionProps: {
children: "Undo",
onClick: () => manager.add({ title: "Invitation withdrawn" }),
},
})
}
>
Custom layout
</Button>
)
}
function Example({
title,
children,
}: {
title: string
children: React.ReactNode
}) {
return (
<section className="flex flex-col gap-3">
<h2 className="text-sm font-medium text-muted-foreground">{title}</h2>
<div className="flex flex-wrap items-start gap-4">{children}</div>
</section>
)
}
export default function Demo() {
return (
<div className="flex flex-col gap-10 p-6">
<Toaster />
<Example title="Default">
<Button
variant="outline"
onClick={() =>
toast.add({
title: "Event created",
description: "Sunday, December 3 at 9:00 AM",
})
}
>
Show toast
</Button>
<Button
variant="outline"
onClick={() =>
toast.add({
title: "Page deleted",
description: "You can restore it for 30 days.",
actionProps: {
children: "Undo",
onClick: () => toast.add({ title: "Page restored" }),
},
})
}
>
With action
</Button>
</Example>
<Example title="Types">
{(["success", "info", "warning", "error", "loading"] as const).map(
(type) => (
<Button
key={type}
variant="outline"
className="capitalize"
onClick={() =>
toast.add({
type,
title: `A ${type} toast`,
description: "Its icon follows the type.",
})
}
>
{type}
</Button>
)
)}
</Example>
<Example title="Promise and close all">
<Button
variant="outline"
onClick={() =>
toast.promise(
new Promise<string>((resolve) =>
setTimeout(() => resolve("Homepage"), 2000)
),
{
loading: { title: "Publishing" },
success: (name) => ({ title: `${name} is live` }),
error: { title: "Publishing failed" },
}
)
}
>
Publish
</Button>
<Button variant="ghost" onClick={() => toast.close()}>
Close all
</Button>
</Example>
<Example title="Custom provider, viewport and layout">
<ToastProvider toastManager={customToast} limit={2}>
<CustomToastButton />
<ToastPortal>
<ToastViewport className="sm:right-auto sm:left-4">
<CustomToastList />
</ToastViewport>
</ToastPortal>
</ToastProvider>
</Example>
</div>
)
}API Reference
Source: react/src/components/ui/toast.tsx
See the Base UI Toast documentation for manager options, stacking, swipe dismissal, and the primitive API.