# Shadcn Sonner (Toast)

> Free shadcn/ui toast component for React — built on Sonner. Success/error/info/warning, descriptions, action buttons, promise (loading→success/error), rich colors, and fully custom toasts. The toast that replaced the deprecated shadcn Toast.

Source: https://designrevision.com/components/sonner

The **Shadcn Sonner** is the toast — built on <a href="https://sonner.emilkowal.ski/" rel="nofollow">**Sonner**</a>, the toast library by Emil Kowalski. shadcn **deprecated its old Toast** component in favour of Sonner, so this is the current, recommended way to show toasts. The examples below cover the whole `toast()` API: types, descriptions, actions, promises, rich colors, and custom toasts.

## Built on Sonner

The shadcn layer is a thin `<Toaster />` wrapper over <a href="https://sonner.emilkowal.ski/" rel="nofollow">Sonner</a> that themes it to your design tokens (via CSS variables) and follows your light/dark theme. Install it:

```bash
npm install sonner
```

(The shadcn component also pulls in `next-themes` for theme detection; it falls back to the system theme if you don't use a theme provider.)

## Mount the Toaster once

Sonner is **API-driven**: you mount one `<Toaster />` near the **root** of your app — typically in `app/layout.tsx` — and then call `toast()` from anywhere:

```tsx
// app/layout.tsx
<body>
  {children}
  <Toaster />
</body>
```

The examples on this page each render their own `Toaster` so they work standalone, but your app needs only one.

## The toast() API

Call `toast` from `"sonner"`:

- **Basic** — `toast("Event has been created", { description: "…" })`.
- **Types** — `toast.success(...)`, `toast.error(...)`, `toast.info(...)`, `toast.warning(...)` (the Types example).
- **Action** — `toast("Moved to trash", { action: { label: "Undo", onClick } })` (the With action example).
- **Promise** — `toast.promise(p, { loading, success, error })` swaps a loading toast for success or error when the promise settles (the Promise example).
- **Custom** — `toast.custom((id) => <YourJSX />)` for a fully custom toast (the Custom JSX example).

## Configuring the Toaster

Options that affect the **whole stack** go on `<Toaster />`, not individual toasts:

```tsx
<Toaster position="top-center" richColors closeButton duration={4000} />
```

`position` moves the stack, `richColors` gives each type a colored surface, `closeButton` adds an X, and `duration` controls how long toasts persist. The Rich colors & close example shows `richColors` + `closeButton`.

## Migrating from the old Toast

If you used shadcn's old `useToast()` / `<Toast>` API, Sonner replaces it: drop the `<ToastProvider>` and `<Toaster />` from `@/components/ui/toast`, mount the Sonner `<Toaster />`, and replace `toast({ title, description })` calls with `toast(title, { description })`. It's less boilerplate and the same idea.

## Accessibility & theming

Sonner manages focus, screen-reader announcements (toasts are announced politely), and keyboard dismissal. Because it's a notification surface, keep messages short and don't put the only copy of critical information in a toast. The toast reads your design tokens — the wrapper maps Sonner's `--normal-bg` / `--normal-text` / `--normal-border` to `--popover` / `--popover-foreground` / `--border` — so it follows your theme, including dark mode, with no overrides.

## Installation

### sonner

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner
```

**Dependencies:** sonner, next-themes, utils

**Usage & accessibility:** The shadcn toast — a <Toaster /> wrapper over Sonner (the toast library by emilkowalski), themed to your design tokens via CSS vars. It is API-driven: mount <Toaster /> once near the root of your app, then call toast(), toast.success(), toast.error(), toast.promise(), or toast.custom() from anywhere. Toaster props (position, richColors, closeButton, duration) configure the stack. Needs next-themes for theme detection; it falls back to "system" without a ThemeProvider.

```tsx
// components/ui/sonner.tsx
"use client"

import { useTheme } from "next-themes"
import { Toaster as Sonner, ToasterProps } from "sonner"

const Toaster = ({ ...props }: ToasterProps) => {
  const { theme = "system" } = useTheme()

  return (
    <Sonner
      theme={theme as ToasterProps["theme"]}
      className="toaster group"
      style={
        {
          "--normal-bg": "var(--popover)",
          "--normal-text": "var(--popover-foreground)",
          "--normal-border": "var(--border)",
        } as React.CSSProperties
      }
      {...props}
    />
  )
}

export { Toaster }
```

## Examples

### Sonner

The canonical toast — a button that calls `toast()` with a title and a description.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-demo-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-demo-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerDemo01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Button
        variant="outline"
        onClick={() =>
          toast("Event has been created", {
            description: "Sunday, December 3, 2025 at 9:00 AM",
          })
        }
      >
        Show toast
      </Button>
      {/* Mount <Toaster /> once near the root of your app — shown here per example. */}
      <Toaster />
    </div>
  )
}
```

---

### Types

Success, error, info, and warning toasts via `toast.success` / `.error` / `.info` / `.warning`.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-types-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-types-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerTypes01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.success("Changes saved")}
        >
          Success
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.error("Something went wrong")}
        >
          Error
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.info("A new update is available")}
        >
          Info
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.warning("Your trial ends in 3 days")}
        >
          Warning
        </Button>
      </div>
      <Toaster />
    </div>
  )
}
```

---

### With description

A toast with a title and a secondary description line.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-description-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-description-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerDescription01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Button
        variant="outline"
        onClick={() =>
          toast.success("Payment received", {
            description: "We've emailed a receipt to ada@example.com.",
          })
        }
      >
        Show toast
      </Button>
      <Toaster />
    </div>
  )
}
```

---

### With action

A toast with an **Undo** action button and a dismiss — the classic undo pattern.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-action-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-action-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerAction01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Button
        variant="outline"
        onClick={() =>
          toast("File moved to trash", {
            action: {
              label: "Undo",
              onClick: () => toast.success("File restored"),
            },
            cancel: {
              label: "Dismiss",
              onClick: () => {},
            },
          })
        }
      >
        Show toast
      </Button>
      <Toaster />
    </div>
  )
}
```

---

### Promise

`toast.promise` ties a toast to an async request — loading, then success or error.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-promise-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-promise-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

function uploadFile() {
  return new Promise<{ name: string }>((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.3) {
        resolve({ name: "report.pdf" })
      } else {
        reject(new Error("Network error"))
      }
    }, 1500)
  })
}

export default function SonnerPromise01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Button
        variant="outline"
        onClick={() =>
          toast.promise(uploadFile(), {
            loading: "Uploading…",
            success: (data) => `${data.name} uploaded`,
            error: "Upload failed — please try again",
          })
        }
      >
        Show toast
      </Button>
      <Toaster />
    </div>
  )
}
```

---

### Rich colors & close

A `<Toaster richColors closeButton>` — colored type surfaces with a close button.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-rich-01
```

**Dependencies:** sonner, button

```tsx
// components/ui/sonner-rich-01.tsx
"use client"

import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerRich01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <div className="flex flex-wrap items-center justify-center gap-2">
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.success("Profile updated")}
        >
          Success
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.error("Card declined")}
        >
          Error
        </Button>
        <Button
          variant="outline"
          size="sm"
          onClick={() => toast.warning("Storage almost full")}
        >
          Warning
        </Button>
      </div>
      {/* richColors gives each type a colored surface; closeButton adds an X. */}
      <Toaster richColors closeButton />
    </div>
  )
}
```

---

### Custom JSX

`toast.custom` renders fully custom content — your own icon, layout, and dismiss button.

**Install:**

```bash
npx shadcn@latest add @designrevision/sonner-custom-01
```

**Dependencies:** lucide-react, sonner, button

```tsx
// components/ui/sonner-custom-01.tsx
"use client"

import { CheckCircle2Icon } from "lucide-react"
import { toast } from "sonner"

import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"

export default function SonnerCustom01() {
  return (
    <div className="flex flex-col items-center gap-4">
      <Button
        variant="outline"
        onClick={() =>
          toast.custom((id) => (
            <div className="bg-popover text-popover-foreground flex w-[340px] items-center gap-3 rounded-md border p-4 shadow-lg">
              <CheckCircle2Icon className="size-5 shrink-0 text-emerald-500" />
              <div className="flex-1">
                <p className="text-sm font-medium">Backup complete</p>
                <p className="text-muted-foreground text-xs">
                  Your workspace was backed up to the cloud.
                </p>
              </div>
              <Button
                variant="ghost"
                size="sm"
                className="-mr-1 h-7"
                onClick={() => toast.dismiss(id)}
              >
                Close
              </Button>
            </div>
          ))
        }
      >
        Show toast
      </Button>
      <Toaster />
    </div>
  )
}
```
