Innovations
modals /

Confirm / Destructive Dialog

Confirmation dialog for irreversible actions. Features a warning icon, loading state during deletion, and proper destructive button styling.

Preview

Source

tsx
"use client";

import { useState } from "react";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
  DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Trash2, AlertTriangle } from "lucide-react";

export default function ConfirmDialog() {
  const [open, setOpen] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const [deleted, setDeleted] = useState(false);

  function handleDelete() {
    setDeleting(true);
    setTimeout(() => {
      setDeleting(false);
      setDeleted(true);
      setOpen(false);
    }, 900);
  }

  return (
    <div className="flex min-h-[200px] items-center justify-center p-8">
      {deleted ? (
        <div className="text-center space-y-2">
          <p className="text-sm text-muted-foreground">Project deleted.</p>
          <Button variant="outline" size="sm" onClick={() => setDeleted(false)}>
            Reset demo
          </Button>
        </div>
      ) : (
        <Dialog open={open} onOpenChange={setOpen}>
          <DialogTrigger asChild>
            <Button variant="destructive" className="gap-2">
              <Trash2 className="h-4 w-4" />
              Delete project
            </Button>
          </DialogTrigger>

          <DialogContent className="sm:max-w-sm">
            <DialogHeader>
              <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-red-100 text-red-600">
                <AlertTriangle className="h-6 w-6" />
              </div>
              <DialogTitle className="text-center text-lg">Delete project?</DialogTitle>
              <DialogDescription className="text-center text-sm">
                This action <strong>cannot be undone</strong>. All data, files, and team
                memberships associated with this project will be permanently removed.
              </DialogDescription>
            </DialogHeader>

            <DialogFooter className="sm:flex-row gap-2 mt-2">
              <Button
                variant="outline"
                className="flex-1"
                onClick={() => setOpen(false)}
                disabled={deleting}
              >
                Cancel
              </Button>
              <Button
                variant="destructive"
                className="flex-1 gap-2"
                onClick={handleDelete}
                disabled={deleting}
              >
                {deleting ? (
                  <>
                    <span className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
                    Deleting…
                  </>
                ) : (
                  <>
                    <Trash2 className="h-4 w-4" />
                    Yes, delete
                  </>
                )}
              </Button>
            </DialogFooter>
          </DialogContent>
        </Dialog>
      )}
    </div>
  );
}
Claude Code Instructions

CLI Install

npx innovations add confirm

Where to use it

Use this before any irreversible action: deleting records, cancelling subscriptions, removing team members, etc. Pattern: 1. Wrap your trigger with <DialogTrigger asChild> 2. Show clear consequences in DialogDescription 3. Use Button variant="destructive" for the confirm action 4. Disable both buttons while the async operation is in progress 5. Close the dialog only after the async operation resolves The AlertTriangle icon and red icon container communicate danger at a glance.