# Toast
URL: /design/components/toast

A passing message for outcomes: success, error, and undo.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

\[interactive preview component ToastSpecimen omitted]

Code for ToastSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";

function ToastSpecimen(): ReactNode {
  return (
    <Button
      variant="outline"
      onClick={() =>
        toast("Thread archived", {
          description: "It stays in the archive until you restore it.",
          action: { label: "Undo", onClick: () => toast("Thread restored") },
        })
      }
    >
      Archive thread
    </Button>
    <Button variant="outline" onClick={() => toast.success("Command copied")}>
      Copy command
    </Button>
    <Button
      variant="outline"
      onClick={() => toast.error("The model did not respond")}
    >
      Fail a request
    </Button>
  );
}
```

The wrapper restyles [Sonner](https://sonner.emilkowal.ski) and is identical in both flavors. Typed toasts carry one hue across the ring, the faint fill, and the icon, while the message itself stays in ink. The card keeps the overlay radius: a toast floats above the page, so it rounds like every lifted surface in the kit.

## Installation

Install the dependencies:

- packages

  - next-themes
  - sonner

Copy the source into `components/ui/sonner.tsx`. Registry items that depend on it install it automatically.

- code

  "use client"; import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, } from "lucide-react"; import { useTheme } from "next-themes"; import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ toastOptions, ...props }: ToasterProps) => { const { classNames: userClassNames, ...toastOptionsRest } = toastOptions ?? {}; const { theme = "system" } = useTheme() as { theme: "light" | "dark" | "system"; }; return ( \<Sonner theme={theme} className="toaster group" icons={{ success: ( \<CircleCheckIcon className="size-4 text-green-600 dark:text-green-400" /> ), info: \<InfoIcon className="size-4 text-blue-600 dark:text-blue-400" />, warning: ( \<TriangleAlertIcon className="size-4 text-amber-600 dark:text-amber-400" /> ), error: \<OctagonXIcon className="text-destructive size-4" />, loading: ( \<Loader2Icon className="text-muted-foreground size-4 animate-spin" /> ), }} toastOptions={{ unstyled: true, classNames: { toast: "bg-popover text-popover-foreground ring-foreground/10 flex w-(--width) items-center gap-2.5 rounded-xl p-3.5 font-sans ring-1 shadow-\[0\_8px\_24px\_-12px\_rgb(0\_0\_0/0.25)] data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_8%,var(--popover))] data-\[type=success]:ring-green-600/40 dark:data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_14%,var(--popover))] dark:data-\[type=success]:ring-green-500/40 data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_8%,var(--popover))] data-\[type=error]:ring-destructive/45 dark:data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_14%,var(--popover))] data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_8%,var(--popover))] data-\[type=warning]:ring-amber-600/40 dark:data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_14%,var(--popover))] dark:data-\[type=warning]:ring-amber-500/40 data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_8%,var(--popover))] data-\[type=info]:ring-blue-500/40 dark:data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_14%,var(--popover))]", content: "flex min-w-0 flex-col gap-0.5", title: "text-sm font-medium", description: "text-muted-foreground text-xs", icon: "relative grid size-4 shrink-0 place-items-center", actionButton: "bg-background border-foreground/15 hover:border-foreground/30 ms-auto h-6 shrink-0 cursor-pointer rounded-md border px-2 text-xs font-medium transition-colors", cancelButton: "text-muted-foreground hover:text-foreground ms-auto h-6 shrink-0 cursor-pointer rounded-md px-2 text-xs transition-colors", closeButton: "text-muted-foreground hover:text-foreground order-last ms-2 grid size-5 shrink-0 cursor-pointer place-items-center rounded-md transition-colors", ...userClassNames, }, ...toastOptionsRest, }} {...props} /> ); }; export { Toaster };

* title

  components/ui/sonner.tsx

* copyText

  "use client"; import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, } from "lucide-react"; import { useTheme } from "next-themes"; import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ toastOptions, ...props }: ToasterProps) => { const { classNames: userClassNames, ...toastOptionsRest } = toastOptions ?? {}; const { theme = "system" } = useTheme() as { theme: "light" | "dark" | "system"; }; return ( \<Sonner theme={theme} className="toaster group" icons={{ success: ( \<CircleCheckIcon className="size-4 text-green-600 dark:text-green-400" /> ), info: \<InfoIcon className="size-4 text-blue-600 dark:text-blue-400" />, warning: ( \<TriangleAlertIcon className="size-4 text-amber-600 dark:text-amber-400" /> ), error: \<OctagonXIcon className="text-destructive size-4" />, loading: ( \<Loader2Icon className="text-muted-foreground size-4 animate-spin" /> ), }} toastOptions={{ unstyled: true, classNames: { toast: "bg-popover text-popover-foreground ring-foreground/10 flex w-(--width) items-center gap-2.5 rounded-xl p-3.5 font-sans ring-1 shadow-\[0\_8px\_24px\_-12px\_rgb(0\_0\_0/0.25)] data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_8%,var(--popover))] data-\[type=success]:ring-green-600/40 dark:data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_14%,var(--popover))] dark:data-\[type=success]:ring-green-500/40 data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_8%,var(--popover))] data-\[type=error]:ring-destructive/45 dark:data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_14%,var(--popover))] data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_8%,var(--popover))] data-\[type=warning]:ring-amber-600/40 dark:data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_14%,var(--popover))] dark:data-\[type=warning]:ring-amber-500/40 data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_8%,var(--popover))] data-\[type=info]:ring-blue-500/40 dark:data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_14%,var(--popover))]", content: "flex min-w-0 flex-col gap-0.5", title: "text-sm font-medium", description: "text-muted-foreground text-xs", icon: "relative grid size-4 shrink-0 place-items-center", actionButton: "bg-background border-foreground/15 hover:border-foreground/30 ms-auto h-6 shrink-0 cursor-pointer rounded-md border px-2 text-xs font-medium transition-colors", cancelButton: "text-muted-foreground hover:text-foreground ms-auto h-6 shrink-0 cursor-pointer rounded-md px-2 text-xs transition-colors", closeButton: "text-muted-foreground hover:text-foreground order-last ms-2 grid size-5 shrink-0 cursor-pointer place-items-center rounded-md transition-colors", ...userClassNames, }, ...toastOptionsRest, }} {...props} /> ); }; export { Toaster };

* viewportClassName

  max-h-\[450px]

- language

  tsx

- code

  "use client"; import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, } from "lucide-react"; import { useTheme } from "next-themes"; import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ toastOptions, ...props }: ToasterProps) => { const { classNames: userClassNames, ...toastOptionsRest } = toastOptions ?? {}; const { theme = "system" } = useTheme() as { theme: "light" | "dark" | "system"; }; return ( \<Sonner theme={theme} className="toaster group" icons={{ success: ( \<CircleCheckIcon className="size-4 text-green-600 dark:text-green-400" /> ), info: \<InfoIcon className="size-4 text-blue-600 dark:text-blue-400" />, warning: ( \<TriangleAlertIcon className="size-4 text-amber-600 dark:text-amber-400" /> ), error: \<OctagonXIcon className="text-destructive size-4" />, loading: ( \<Loader2Icon className="text-muted-foreground size-4 animate-spin" /> ), }} toastOptions={{ unstyled: true, classNames: { toast: "bg-popover text-popover-foreground ring-foreground/10 flex w-(--width) items-center gap-2.5 rounded-xl p-3.5 font-sans ring-1 shadow-\[0\_8px\_24px\_-12px\_rgb(0\_0\_0/0.25)] data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_8%,var(--popover))] data-\[type=success]:ring-green-600/40 dark:data-\[type=success]:bg-\[color-mix(in\_oklab,var(--color-green-500)\_14%,var(--popover))] dark:data-\[type=success]:ring-green-500/40 data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_8%,var(--popover))] data-\[type=error]:ring-destructive/45 dark:data-\[type=error]:bg-\[color-mix(in\_oklab,var(--destructive)\_14%,var(--popover))] data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_8%,var(--popover))] data-\[type=warning]:ring-amber-600/40 dark:data-\[type=warning]:bg-\[color-mix(in\_oklab,var(--color-amber-500)\_14%,var(--popover))] dark:data-\[type=warning]:ring-amber-500/40 data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_8%,var(--popover))] data-\[type=info]:ring-blue-500/40 dark:data-\[type=info]:bg-\[color-mix(in\_oklab,var(--color-blue-500)\_14%,var(--popover))]", content: "flex min-w-0 flex-col gap-0.5", title: "text-sm font-medium", description: "text-muted-foreground text-xs", icon: "relative grid size-4 shrink-0 place-items-center", actionButton: "bg-background border-foreground/15 hover:border-foreground/30 ms-auto h-6 shrink-0 cursor-pointer rounded-md border px-2 text-xs font-medium transition-colors", cancelButton: "text-muted-foreground hover:text-foreground ms-auto h-6 shrink-0 cursor-pointer rounded-md px-2 text-xs transition-colors", closeButton: "text-muted-foreground hover:text-foreground order-last ms-2 grid size-5 shrink-0 cursor-pointer place-items-center rounded-md transition-colors", ...userClassNames, }, ...toastOptionsRest, }} {...props} /> ); }; export { Toaster };

## Usage

Mount one `Toaster` at the root, then call `toast` from anywhere. This site mounts it in the root provider.

```
import { Toaster } from "@/components/ui/sonner";

export function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <>
      {children}
      <Toaster position="top-center" />
    </>
  );
}
```

```
import { toast } from "sonner";

toast.success("Command copied");
toast("Thread archived", {
  description: "It stays in the archive until you restore it.",
  action: { label: "Undo", onClick: restore },
});
```

## Examples

### Types and promises

`info` and `warning` follow the callout hues. `toast.promise` renders the loading state with the kit's one sanctioned spinner, then settles into success or error.

\[interactive preview component ToastTypesSpecimen omitted]

Code for ToastTypesSpecimen preview:

```tsx
"use client";

import { useState, type ReactNode } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";

function ToastTypesSpecimen(): ReactNode {
  return (
    <Button
      variant="outline"
      onClick={() => toast.info("A new version of the CLI is available")}
    >
      Info
    </Button>
    <Button
      variant="outline"
      onClick={() => toast.warning("This adapter needs an API key")}
    >
      Warning
    </Button>
    <Button
      variant="outline"
      onClick={() =>
        toast.promise(new Promise((resolve) => setTimeout(resolve, 1600)), {
          loading: "Exporting thread…",
          success: "Exported as markdown",
          error: "Export failed",
        })
      }
    >
      Run a promise
    </Button>
  );
}
```

## API Reference

### Toaster

Wraps Sonner's `Toaster` and accepts all of its props; the wrapper only sets the house styling through `toastOptions.classNames` and reads the theme from `next-themes`.

- `position`: `"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"` (default `"bottom-right"`) — Where the stack lives. This site uses top-center.
- `toastOptions?`: `ToastOptions` — Per-toast defaults. Class overrides merge after the house styles.
- `closeButton`: `boolean` (default `false`) — Adds a dismiss button to every toast.

### toast

Import `toast` from `sonner` directly: `toast(title, options)`, `toast.success`, `toast.error`, `toast.info`, `toast.warning`, and `toast.promise` are the calls this kit styles.