---
title: Dialog confirm
description: Asks for confirmation before a destructive or irreversible action, optionally by typing a word.
---

```tsx
import { Button, Dialog, DialogConfirm, type DialogConfirmPayload } from "@openperf/deft";

import { Preview } from "../../src/Preview";

const HANDLE = Dialog.createHandle<DialogConfirmPayload>();

export default function DialogConfirmBasic() {
  return (
    <Preview overlay={true}>
      <Dialog.Trigger
        handle={HANDLE}
        payload={{ title: "Title", description: "Description", onConfirm: () => HANDLE.close() }}
        render={<Button.Root />}
      >
        Open dialog
      </Dialog.Trigger>
      <DialogConfirm handle={HANDLE} />
    </Preview>
  );
}
```

- Use it before an action that deletes data or can't be undone. Use a [Dialog](/deft/components/dialog) for anything that needs a form.
- Create a handle with `Dialog.createHandle` and pass it to every `Dialog.Trigger` and to one `DialogConfirm`.
- The dialog's content comes from the `payload` of the trigger that opened it, so one `DialogConfirm` serves every trigger on the page.
- A destructive confirm button is `danger` with a verb label, such as "Delete". The button that opens the dialog stays `default` or `ghost`.
- Close the dialog from `onConfirm` with `handle.close()` once the action is done.
- While `pending` is true, the confirm button shows a spinner, Cancel is disabled, and Escape and outside clicks don't close the dialog.

## Typed confirmation

```tsx
import { Button, Dialog, DialogConfirm, type DialogConfirmPayload } from "@openperf/deft";

import { Preview } from "../../src/Preview";

const HANDLE = Dialog.createHandle<DialogConfirmPayload>();

export default function DialogConfirmExpected() {
  return (
    <Preview overlay={true}>
      <Dialog.Trigger
        handle={HANDLE}
        payload={{
          title: "Title",
          description: "Description",
          expected: "Value",
          onConfirm: () => HANDLE.close(),
        }}
        render={<Button.Root />}
      >
        Open dialog
      </Dialog.Trigger>
      <DialogConfirm handle={HANDLE} />
    </Preview>
  );
}
```

Set `expected` in the payload to add a field labelled "Type … to confirm". The confirm button stays disabled until the typed text matches; spaces around it are ignored.

## API

| Prop | Type | Default | Description |
| - | - | - | - |
| `handle` | `DialogHandle<DialogConfirmPayload>` | - | From Dialog.createHandle, shared with the triggers. |
| `pending?` | `boolean` | `false` | Shows a spinner and blocks Cancel and dismissal. |
| `children?` | `(payload) => ReactNode` | - | Extra detail rendered under the description. |

### Payload

| Prop | Type | Default | Description |
| - | - | - | - |
| `title` | `ReactNode` | - | |
| `onConfirm` | `() => void` | - | |
| `description?` | `ReactNode` | - | |
| `expected?` | `string` | - | Text the user must type to enable confirmation. |
| `confirmLabel?` | `ReactNode` | `"Confirm"` | |
| `cancelLabel?` | `ReactNode` | `"Cancel"` | |
| `confirmVariant?` | `"default" \| "field" \| "primary" \| "ghost" \| "danger"` | `"primary"` | Variant of the confirm button. |

Built on [Dialog](/deft/components/dialog), [Button](/deft/components/button), [Field](/deft/components/field) and [Input](/deft/components/input).
