Components Overlays
ConfirmationDialog
StableA ready-made confirm/cancel dialog with async loading and error handling, plus an imperative useConfirm() hook.
ConfirmationDialog wraps AlertDialog with the props most confirmations need. When onConfirm returns a Promise, the confirm button shows a spinner, Cancel and Escape are blocked, the dialog closes on success and shows the error message on failure. ConfirmProvider and useConfirm() give you `await confirm({...})` from any event handler, which replaces window.confirm and hand-rolled confirm contexts.
Overview
import { Button, ConfirmationDialog } from '@bindshq/stance-react';
export function Basic() {
return (
<ConfirmationDialog
trigger={<Button variant="danger">Remove user</Button>}
title="Remove Ada Lovelace?"
description="Ada loses access to Binds immediately. Their signatures stay in place."
confirmLabel="Remove user"
tone="danger"
onConfirm={() => {}}
/>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { ConfirmationDialog, ConfirmProvider, useConfirm } from '@bindshq/stance-react';Platforms
ConfirmationDialog has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { ConfirmationDialog, ConfirmProvider, useConfirm } from '@bindshq/stance-react';Examples
Async confirm
Return a Promise from onConfirm. This example fails once, then succeeds.
import * as React from 'react';
import { Button, ConfirmationDialog } from '@bindshq/stance-react';
export function Async() {
const attempts = React.useRef(0);
return (
<ConfirmationDialog
trigger={<Button>Deploy signatures</Button>}
title="Deploy to 128 mailboxes?"
description="Signatures are pushed through your Exchange relay."
confirmLabel="Deploy"
loadingText="Deploying…"
onConfirm={() =>
new Promise<void>((resolve, reject) => {
attempts.current += 1;
setTimeout(() => {
if (attempts.current % 2 === 1)
reject(new Error('Exchange did not respond. Try again.'));
else resolve();
}, 1200);
})
}
/>
);
}useConfirm()
Await the user’s decision inside any handler.
import * as React from 'react';
import { Button, ConfirmProvider, useConfirm } from '@bindshq/stance-react';
function DeleteButton() {
const confirm = useConfirm();
const [status, setStatus] = React.useState('');
return (
<div style={{ display: 'grid', gap: 8, justifyItems: 'center' }}>
<Button
variant="outline"
onClick={async () => {
const ok = await confirm({
title: 'Archive “Q3 campaign”?',
description: 'Archived templates can be restored for 30 days.',
confirmLabel: 'Archive',
});
setStatus(ok ? 'Archived.' : 'Kept.');
}}
>
Archive template
</Button>
<span aria-live="polite">{status}</span>
</div>
);
}
export function Imperative() {
return (
<ConfirmProvider>
<DeleteButton />
</ConfirmProvider>
);
}API reference
ConfirmationDialog
| Prop | Type | Default | Description |
|---|---|---|---|
title* required | ReactNode | None | Question naming the action and object. |
description | ReactNode | None | Consequences of confirming. |
confirmLabel | ReactNode | 'Confirm' | Confirm button text. Prefer a verb. |
cancelLabel | ReactNode | 'Cancel' | Cancel button text. |
tone | 'default' | 'danger' | 'default' | Danger styles the confirm button as destructive. |
onConfirm | () => void | Promise<unknown> | None | Return a Promise for loading state; rejecting keeps the dialog open and shows the error message. |
onCancel | () => void | None | Called on Cancel or Escape. |
loadingText | ReactNode | None | Confirm label while pending. |
trigger | ReactElement | None | Optional trigger, rendered with asChild. |
open / defaultOpen / onOpenChange | boolean / boolean / (open: boolean) => void | None | Controlled or uncontrolled state. |
children | ReactNode | None | Extra content above the buttons. |
ConfirmProvider
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | None | Mount once near the root. |
useConfirm
| Prop | Type | Default | Description |
|---|---|---|---|
returns | (options: ConfirmOptions) => Promise<boolean> | None | Resolves true on confirm, false on cancel. Options: title, description, confirmLabel, cancelLabel, tone. |
Accessibility
- Built on AlertDialog: role="alertdialog", focus starts on Cancel, outside clicks never dismiss.
- While pending the confirm button has aria-busy and Cancel is disabled, so the request cannot be abandoned half-way.
- Errors from a rejected promise are shown in a role="alert" message inside the dialog.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves between Cancel and Confirm. |
| Enter Space | Activates the focused button. |
| Esc | Cancels (ignored while a confirm is pending). |
Usage guidelines
Do
- Use tone="danger" and a specific verb (“Remove user”) for destructive actions.
- Throw an Error with a human message from onConfirm on failure.
Don’t
- Ask for confirmation of easily undone actions; offer Undo in a toast instead.