Destructive confirm
Confirms an action that cannot be undone: it names what is lost, can ask people to type a name first, waits for the async action, shows its error without closing, and explains when the action is blocked.
npx prodular-ui add destructive-confirmType to confirm, errors and blocked
Delete workspace asks for the slug. Remove member fails with a reason and stays open. Delete owner is blocked with an explanation.
"use client"
import { Button } from "@/components/ui/button"import { DestructiveConfirm } from "@/components/ui/destructive-confirm"
export default function DestructiveConfirmWorkspace() { return ( <div className="flex flex-wrap items-center gap-3"> <DestructiveConfirm trigger={<Button variant="destructive-subtle">Delete workspace</Button>} title="Delete Halcyon Labs?" description="The workspace and everything in it are deleted for every member. You cannot undo this." consequences={["4,128 invoices and 312 customers", "Every connected integration and API key", "The Scale subscription, which stops renewing today"]} confirmText="halcyon-labs" confirmLabel="Delete workspace" onConfirm={() => new Promise((r) => setTimeout(r, 900))} /> <DestructiveConfirm trigger={<Button variant="outline">Remove member</Button>} title="Remove Hana Sato?" description="Hana loses access right away. Their invoices and comments stay in the workspace." confirmLabel="Remove" onConfirm={() => new Promise((_, reject) => setTimeout(() => reject(new Error("Hana is the only approver on 3 open payouts. Reassign them first.")), 700)) } /> <DestructiveConfirm trigger={<Button variant="ghost">Delete owner</Button>} title="Delete Maya Okafor?" confirmLabel="Delete" disabledReason="A workspace needs at least one owner. Make someone else an owner first." onConfirm={() => {}} /> </div> )}Anatomy
<DestructiveConfirm trigger={<Button variant="destructive-subtle">Delete workspace</Button>} title="Delete Halcyon Labs?" consequences={["4,128 invoices"]} confirmText="halcyon-labs" onConfirm={deleteWorkspace}/>Guidance
Use it when
- Deleting or revoking things people cannot get back: workspaces, projects, keys, data.
- confirmText for the biggest actions, with a name people have to read: the workspace slug or the key name.
- consequences to list what goes with it, in counts people recognize.
Avoid
- Reversible actions. Do them at once and offer Undo in a toast.
- Typing to confirm for every delete. Keep it for actions that destroy a lot.
Rules for AI agents
Published in llms-full.txt, the registry and the MCP server, so coding agents follow them when they build with this component.
- Write the title as a question that names the thing: Delete Halcyon Labs?
- Make onConfirm async and throw an Error with a message people can act on; the dialog stays open and shows it.
- Pass disabledReason instead of hiding the action when it is not allowed right now, and say how to unblock it.
- The confirm button stays disabled until the typed text matches exactly; do not lowercase or trim the expected value.
API
DestructiveConfirm
| Prop | Type | Default | Description |
|---|---|---|---|
| trigger / open / onOpenChange | ReactElement / boolean / (open) => void | Uncontrolled with a trigger, or controlled. | |
| title / description | ReactNode | The question and what happens. | |
| consequences | ReactNode[] | What is lost, one line each. | |
| confirmText | string | Text to type before the action is enabled. | |
| confirmLabel / cancelLabel | string | "Delete" / "Cancel" | Button labels. |
| onConfirm | () => void | Promise<void> | Runs the action. Throw to show an error. | |
| disabledReason | ReactNode | Blocks the action and says why. |
Installation
The Prodular UI CLI adds the source to components/ui with everything it depends on. In a project that already uses shadcn/ui, the shadcn CLI installs it from the Prodular registry too. See Installation for the first setup.
npx prodular-ui add destructive-confirm# or, with the registry in components.jsonnpx shadcn@latest add @prodular/destructive-confirm