Appearance
ConfirmDialog
Purpose
ConfirmDialog is the shared UI for confirmations. It is intentionally “dumb”:
- It renders a dialog using Radix
AlertDialogprimitives - It receives configuration via
ConfirmOptions - It relies on
useConfirmationto provide the open state and promise-based control flow
When to use
Use ConfirmDialog when:
- You want consistent confirmation UI across the app
- You want async control flow (
await confirm(...)) rather than callback nesting
Don’t embed confirmation logic inside the dialog. Keep logic in useConfirmation.
Example
tsx
import React from 'react'
import { useConfirmation } from '@/shared/hooks'
import { ConfirmDialog } from '@/shared/ui/components/ConfirmDialog'
export function DeleteThingButton() {
const { state, confirm, handleConfirm, handleCancel } = useConfirmation()
const onDelete = async () => {
const ok = await confirm({
title: 'Delete item?',
description: 'This action cannot be undone.',
confirmLabel: 'Delete',
cancelLabel: 'Cancel',
variant: 'destructive',
})
if (!ok) return
// await repository.deleteThing()
}
return (
<>
<button type="button" onClick={onDelete}>Delete</button>
<ConfirmDialog
open={state.isOpen}
options={state.options}
onConfirm={handleConfirm}
onCancel={handleCancel}
/>
</>
)
}Props (concise)
open: boolean- Whether the dialog is visible.
options: ConfirmOptions | null- Configuration for the dialog.
ConfirmOptionssupports:title,description?,confirmLabel?,cancelLabel?,variant?: 'default' | 'destructive'.
onConfirm()- Called when the user confirms.
onCancel()- Called when the user cancels, or when the dialog is dismissed.
Notes / Gotchas
- Always render one instance: treat
ConfirmDialoglike an app/page-level portal for confirmations, driven byuseConfirmationstate. options === null: the component returnsnullifoptionsis missing; ensure you passstate.optionsfrom the hook.- Dismiss behavior: closing the dialog triggers cancel logic so
confirm(...)resolvesfalse.