react-call v2

Confirm dialog

Ask the user to confirm a destructive action before it runs. Returns a boolean to the caller.

→ awaiting click…

The Callable

declared once, mounted in the React tree
Confirm.tsx
import { createCallable } from 'react-call'
interface Props {
message: string
}
type Response = boolean
export const Confirm = createCallable<Props, Response>(({ call, message }) => (
<div
role="dialog"
aria-modal="true"
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
>
<div className="w-full max-w-sm rounded-lg border border-[var(--color-border)] bg-[var(--color-bg)] p-6 shadow-2xl">
<p className="text-base text-[var(--color-fg)]">{message}</p>
<div className="mt-6 flex items-center justify-end gap-3">
<button
type="button"
onClick={() => call.end(false)}
className="rounded-md border border-[var(--color-border)] px-4 py-2 text-sm font-medium text-[var(--color-fg-muted)] transition-colors hover:text-[var(--color-fg)]"
>
Cancel
</button>
<button
type="button"
onClick={() => call.end(true)}
className="rounded-md bg-[var(--color-accent)] px-4 py-2 text-sm font-medium text-[var(--color-accent-fg)] transition-colors hover:bg-[var(--color-accent-hover)]"
>
Continue
</button>
</div>
</div>
</div>
))
Confirm.displayName = 'Confirm'

The Root

mounted in your app tree, once
App.tsx
import { Confirm } from './Confirm'
import { DeleteButton } from './DeleteButton'
export default function App() {
return (
<>
<Confirm />
<DeleteButton />
</>
)
}

The caller

anywhere in your app, imperative
DeleteButton.tsx
import { useState } from 'react'
import { Confirm } from './Confirm'
export const DeleteButton = () => {
const [status, setStatus] = useState<'idle' | 'deleted' | 'cancelled'>('idle')
const handleClick = async () => {
const accepted = await Confirm.call({
message: 'Delete this item? This action cannot be undone.',
})
setStatus(accepted ? 'deleted' : 'cancelled')
}
return (
<div className="flex flex-col items-center gap-3">
<button
type="button"
onClick={handleClick}
className="rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-red-700"
>
Delete item
</button>
<span className="font-mono text-xs text-[var(--color-fg-subtle)]">
{status === 'idle' && '→ awaiting click…'}
{status === 'deleted' && (
<span className="text-[var(--color-accent)]">→ deleted</span>
)}
{status === 'cancelled' && '→ cancelled'}
</span>
</div>
)
}

A boolean is the whole answer

A boolean response is the right model when the user’s answer is the whole point. Delete confirmations, “discard unsaved changes?”, “do you accept?” — the caller doesn’t need a payload back, just a yes-or-no.

If the dialog has fields, return the form data directly (see Prompt for input or Save form) instead of a boolean plus a side-channel.

Related examples