Skip to content
goodtabs UI
BrowseBrowse components

Undo toast

A Sonner toast with an Undo action that either restores or commits the change.

npx shadcn@latest add https://ui.goodtabs.co/r/undo-toast.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/undo-toast

  • Motion references
  • Type specimens
  • Icon sets

The row goes away at once. Undo puts it back in the same place.

Commit on close

Draft is live

onCommit runs once the toast closes without an undo.

Usage

import { undoToast } from "@/components/ui/undo-toast"

undoToast("Removed Motion references", {
  onUndo: () => restore(item),
  onCommit: () => api.delete(item.id),
})

Props

PropTypeDefaultDescription
messagestringFirst argument. The toast title.
onUndo() => voidRuns when Undo is pressed.
onCommit() => voidRuns once if the toast closes any other way.
descriptionstringOptional muted line.
durationnumber6000Milliseconds before it closes.

Source

undo-toast/undo-toast.tsx
"use client";

import { toast } from "sonner";

type UndoToastOptions = {
  /** Muted line under the message. */
  description?: string;
  /** Runs when the person presses Undo. Restore the item here. */
  onUndo: () => void;
  /** Runs once if the toast closes without an undo. Make the change permanent here. */
  onCommit?: () => void;
  /** Milliseconds before the toast closes on its own. */
  duration?: number;
};

/**
 * Shows a toast with an Undo action. Exactly one of onUndo or onCommit runs.
 * Mount the Toaster from the toaster item once, then call this anywhere.
 */
function undoToast(message: string, { description, onUndo, onCommit, duration = 6000 }: UndoToastOptions) {
  let undone = false;
  const commit = () => {
    if (!undone) onCommit?.();
  };
  return toast(message, {
    description,
    duration,
    action: {
      label: "Undo",
      onClick: () => {
        undone = true;
        onUndo();
      },
    },
    onDismiss: commit,
    onAutoClose: commit,
  });
}

export { undoToast };

For AI agents

Hand your agent this page, the markdown at /components/undo-toast/prompt.md, or the Copy AI prompt button above.

When to use

  • Reversible actions like removing, archiving or unpublishing
  • Instead of a confirm dialog, which interrupts the flow

When not to use

  • Actions that cannot be reversed: ask first with an alert dialog
  • Plain confirmations: use toast.success from the toaster

Agent notes

  • Apply the change optimistically, then call undoToast; do the real delete in onCommit
  • Needs the Toaster from the toaster item mounted once in the layout
  • Exactly one of onUndo or onCommit runs. A dismissed toast counts as a commit
  • Sonner makes the Undo button keyboard reachable and announces the toast