Popconfirm
Preview: Default
Lightweight inline confirmation: the trigger opens an alertdialog popover anchored to it. Use for reversible-ish actions; use AlertDialog (modal) for destructive ones that can't be undone.
Import
import { Popconfirm } from '@marbleui/ui'Props
<Popconfirm>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactElement<unknown, string | JSXElementConstructor<any>> | — | |
| onConfirm *required | () => void | Promise<void> | — | |
| title *required | ReactNode | — | |
| cancelLabel | string | 'Cancel' | |
| confirmLabel | string | 'Confirm' | |
| description | ReactNode | — | |
| destructive | boolean | — | |
| placement | 'bottom' | 'top' | 'top' |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Dialog · React Aria DialogTrigger · React Aria Heading. Follow the primitive's docs for the full keyboard map.
Usage notes
Inline confirmation anchored to the button that triggered it: a small popover with a title, optional description, and Cancel/Confirm. For actions that can’t be undone use the modal AlertDialog.
<Popconfirm title="Archive this issue?" description="You can restore it from the archive." confirmLabel="Archive" onConfirm={() => archiveIssue(issue.id)}>
<Button size="sm" variant="ghost">Archive</Button>
</Popconfirm>
- The child must be one pressable element, such as
ButtonorIconButton. onConfirmmay return a promise: the confirm button shows “Working…” and is disabled until it settles, then the popover closes. If it rejects, the popover stays open so the user can retry or cancel; the error is logged, so report it yourself, for example with atoast.- Focus starts on Cancel, so a stray Enter doesn’t confirm; Escape or clicking outside also cancels. The popover is a
role="alertdialog"named bytitle. destructiveswitches the confirm button and icon to danger colors.placementistop(default) orbottom.
Example source · show· hide
import { useState } from 'react'
import { Button } from '../button'
import { Popconfirm } from './popconfirm'
function Demo() {
const [items, setItems] = useState(['ACME-142 Fix login redirect', 'ACME-139 Audit color contrast', 'ACME-151 Billing empty state'])
return (
<ul className="w-96 divide-y divide-border rounded-xl border border-border bg-surface">
{items.map((t) => (
<li key={t} className="flex items-center justify-between gap-2 px-4 py-2 text-sm text-fg">{t}
<Popconfirm title="Archive this issue?" description="You can restore it from the archive." confirmLabel="Archive" onConfirm={() => setItems((s) => s.filter((x) => x !== t))}><Button size="sm" variant="ghost">Archive</Button></Popconfirm>
</li>
))}
</ul>
)
}
export const styles = {
Default: Demo,
Destructive: () => <Popconfirm destructive title="Revoke this API key?" description="Apps using it stop working immediately." confirmLabel="Revoke" onConfirm={() => new Promise((r) => setTimeout(r, 600))}><Button variant="outline">Revoke key</Button></Popconfirm>,
}Source: packages/ui/src/popconfirm/popconfirm.tsx
Catalog
- Category
- overlays
- Priority
- Extra
- Primitive
- composed
- React Aria
- DialogTrigger + Popover
- Base UI
- Popover