# shadcn's toast page wants toast.add with a title, not one call

> shadcn's toast page uses toast.add with a title, not one call. Here is the Toaster setup, a submit example and an undo action in React.
> By Dave · 2026-10-13
> Source: https://otf-kit.dev/blog/shadcn-toast-howto

A success message after a form submit usually looks like one function call. On the shadcn toast page it is `toast.add` with a title, and the Toaster has to sit in your layout before any message shows.

## What the shadcn toast page covers

The page describes the component as "A succinct message that is displayed temporarily." The install command is `pnpm dlx shadcn@latest add toast`. The setup has two parts. A `Toaster` goes once in `app/layout.tsx`, and a `toast` function is imported from the same `@/components/ui/toast` path wherever you need a message.

The usage example calls `toast.add` with a `title` and a `description`. An action, such as an undo button, goes in an `actionProps` object, and `toast.close` takes the id that `toast.add` returns. The page also has a Types section that describes a `type` option and a Promise demo. Both of their code blocks are collapsed on the page, so check the View Code panel before you rely on either one.

The page does not say Toast is deprecated, does not mention Sonner, and does not recommend another library. It has no sentence about when to use a toast. The rules below are ours, and they are labelled as ours.

## Mount the Toaster once

The Toaster renders the stack of messages. Put it in the root layout, inside the body and after your page content, so every route can show a message. Mounting it in a single page means a message fired from another page will not appear.

```tsx
import { Toaster } from "@/components/ui/toast"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <main>{children}</main>
        <Toaster />
      </body>
    </html>
  )
}
```

## Show success or error after a submit

The common case is a form that either saves or fails. Call `toast.add` in both branches, so the user always gets a result. The success message states what happened. The error message states what failed and gives the user something to do next.

```tsx
"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ProfileForm() {
  async function onSave() {
    try {
      await saveProfile() // your save call
      toast.add({
        title: "Profile saved",
        description: "Your changes are live.",
      })
    } catch {
      toast.add({
        title: "Could not save",
        description: "Check your connection and try again.",
      })
    }
  }

  return <Button onClick={onSave}>Save profile</Button>
}
```

Keep the form's input when the save fails. A message that says "could not save" while the form clears has taken the user's work with it. Clear the form only after the success branch runs.

![Luna presses a submit button while a success card appears beside her](https://cdn.otf-kit.dev/blog/shadcn-toast-howto/inbody1-20261013.png)

## Offer an undo

Some actions can be reversed, such as archiving a project. The toast can carry a single action. The page's example passes an `actionProps` object with the button label as `children` and an `onClick` handler. Keep the id that `toast.add` returns, and close the toast from inside the handler after the reversal:

```tsx
const id = toast.add({
  title: "Project archived",
  actionProps: {
    children: "Undo",
    onClick() {
      restoreProject() // your restore call
      toast.close(id)
    },
  },
})
```

Only offer an action the user can actually reverse. If the action cannot be undone, put the warning in a dialog before the change, not in a toast after it.

## Write the wording

Our rule for the copy is short. The title states the result in a few words. The description states the next step, or says nothing when the title is enough. Avoid vague titles such as "Done" or "Error", because the user cannot tell what changed.

Length matters more than most teams expect. A title of a few words reads in one glance. A title of twenty words wraps on a phone and pushes the description below the fold. Keep titles short and move detail into the description. If a message needs three sentences, it is probably a dialog or a line on the page, not a toast.

Say what happened in plain words. "Profile saved" states a result. "Success!" adds nothing, and the exclamation mark makes a routine save sound like an event. Avoid stacking the same error on every retry. Let the user act on the first message before you add another one.

Before you use the `type` option for success and error styling, read its code on the page. The code is collapsed, so the example you can see does not show it. Until you have checked it, the title and description carry the meaning on their own.

![Dex and Luna watching a check card leave at the screen edge](https://cdn.otf-kit.dev/blog/shadcn-toast-howto/inbody2-20261013.png)

## Test before you ship

Toasts fail where the network, the screen size and the keyboard meet. Check these before you ship:

- Turn the network off and submit. The error toast should show, and the form should keep the user's input.
- Submit twice quickly. The success toast should show once for one save, and the button should not allow a second submit while the first is running.
- Submit several times in a row. Toasts stack, so check that the stack does not cover the submit button on a 390px phone.
- Tab to the undo action after the toast appears. It should take focus, and pressing Enter should run the reversal.
- Wait for the toast to leave. Anything the user needs later should be on the page, not only in the toast, because the toast is temporary by design.

Run the phone check last, after the wording is final. Long titles wrap, and a wrapped message can push the stack over the page.

## Where OTF stands

If you want a toast or a notification component already built into a product, we cannot point you to one. We checked the SaaS Dashboard, Booking, Fitness and Arcade kit pages for toast, notification and alert terms. None of them names a toast component. The SaaS Dashboard page lists "Inbox & notifications" as a feature. That is a feature of the product, not a toast component, and this article does not claim it is one.

The pricing page lists the Full-stack kits at $99 each and the Everything Bundle at $149, both as one-time payments, and it offers a live demo before you buy. Check each kit's repository for the components it ships before you rely on any of them. For the forms that use these messages, see the [dialog and sheet guide](/blog/shadcn-dialog-vs-sheet-howto), and for the table actions that often need a result message, see the [data table guide](/blog/shadcn-data-table-howto). If you are choosing a starting point for a product, the pricing page is the place to compare kits side by side: [OTF pricing](/pricing).

## Sources

- shadcn/ui, Toast: https://ui.shadcn.com/docs/components/toast
- OTF pricing: [otf-kit.dev/pricing](/pricing)
