# Confirm dialog (/cloud/desktop/confirm-dialog)



```ts title="Import"
import { ConfirmDialog } from "../components/confirm-dialog.tsx";
```

File: `packages/desktop/src/renderer/src/components/confirm-dialog.tsx`, over
[`@nyte-ai/ui/alert-dialog`](/cloud/headless/alert-dialog). Used for: delete session, archive all
chats in a workspace, discard unsaved file, close a running terminal, sign out of GitHub CLI.

## Anatomy [#anatomy]

```
AlertDialog.Root (open, onOpenChange)
└─ Portal
   ├─ Backdrop            layer.dialogBackdrop, t.bgScrim
   └─ Popup               layer.dialog, min(400px, 100vw − 48px), initialFocus=cancel, finalFocus=returnFocusRef, aria-busy
      ├─ Title
      ├─ Description
      ├─ <p role="alert">  error, when present
      └─ actions
         ├─ AlertDialog.Close  (Button secondary)  "Cancel"
         └─ Button danger       confirmLabel | pendingLabel
```

## Props [#props]

| Prop             | Type                                   | Default                                                                    | Description                                                                  |
| ---------------- | -------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `open`           | `boolean`                              |                                                                            | Required, controlled.                                                        |
| `pending`        | `boolean`                              |                                                                            | Required. Disables both buttons, sets `aria-busy`, blocks closing.           |
| `error`          | `string \| undefined`                  |                                                                            | Required. Rendered as `role="alert"` when defined.                           |
| `returnFocusRef` | `RefObject<HTMLButtonElement \| null>` |                                                                            | Required. The control that opened the dialog; focus returns to it on close.  |
| `title`          | `string`                               | `"Delete chat?"`                                                           |                                                                              |
| `description`    | `ReactNode`                            | "This permanently deletes the chat and its history. This can't be undone." |                                                                              |
| `confirmLabel`   | `string`                               | `"Delete"`                                                                 |                                                                              |
| `pendingLabel`   | `string`                               | `"Deleting…"`                                                              |                                                                              |
| `onOpenChange`   | `(open: boolean) => void`              |                                                                            | Required. Only called with `false`, and never while `pending`.               |
| `onConfirm`      | `() => void`                           |                                                                            | Required. The dialog does not close itself; the caller closes after success. |

## Behavior that must be kept [#behavior-that-must-be-kept]

* Initial focus is on **Cancel**, not on the destructive action. Pressing Enter reflexively
  cancels.
* `finalFocus={returnFocusRef}` is required so the row or button that opened the dialog gets focus
  back. Callers pass a ref to the exact element.
* While `pending`, `onOpenChange` is swallowed: Escape and outside press do nothing until the
  mutation settles.
* The error stays inside the dialog as a live `role="alert"`; the dialog stays open so the user can
  retry or cancel.

## Example [#example]

```tsx
const deleteRef = useRef<HTMLButtonElement>(null);
const remove = useMutation(…);

<Button ref={deleteRef} variant="danger" onClick={() => setOpen(true)}>Delete</Button>
<ConfirmDialog
  open={open}
  pending={remove.isPending}
  error={remove.error?.message}
  returnFocusRef={deleteRef}
  onOpenChange={setOpen}
  onConfirm={() => remove.mutate(id, { onSuccess: () => setOpen(false) })}
/>
```

## Compared with `@nyte-ai/ui` AlertDialog [#compared-with-nyte-aiui-alertdialog]

The shared [Alert dialog](/cloud/primitives/alert-dialog) is parts you compose; this is a finished
surface with a fixed footer and a pending state. They share Base UI behavior and nothing else.
