components
Confirm dialog
The confirmation contract in one prop — `destructive`. Past that, name the action like a verb (`Delete report`, `Remove James`) and explain the consequences in one sentence.
Read the full specDestructive
The canonical destructive flow — red confirm button, the async `onConfirm` keeps the dialog open until it resolves.
tsx
const [open, setOpen] = useState(false);
<Button variant="destructive" onClick={() => setOpen(true)}>
Delete report
</Button>
<ConfirmDialog
open={open}
onOpenChange={setOpen}
title="Delete this report?"
description="The report will be removed from the feed. This can't be undone."
confirmLabel="Delete report"
destructive
onConfirm={async () => { await deleteReport(id); }}
/>Non-destructive
Drop `destructive` to get the default (brand-green) confirm button. Good for publishing, sending, locking in state.
Async with loading state
`onConfirm` can return a Promise. The dialog stays open and the confirm label gets an ellipsis while the promise settles.