Skip to content

Confirm Popover

A popover anchored to its trigger with one line about the action and a confirm button.

// Nothing has run yet

npx shadcn@latest add @sureui/confirm-popover

tsx
import { Button } from "@/components/ui/button"import { ConfirmPopover } from "@/components/ui/sureui/confirm-popover"<ConfirmPopover  description="Open pull requests from this branch will close."  confirmLabel="Delete branch"  variant="destructive"  onConfirm={deleteBranch}>  <Button variant="outline">Delete branch</Button></ConfirmPopover>

Cancel, Escape, a click outside or tabbing out close it and call onCancel.

variant styles only the confirm button. The trigger is your own element, so an outline trigger can open a destructive confirm step, as above.

Hold

gesture works as on Confirm Button, here with "hold".

// Nothing has run yet

Title

Add a title to show a heading above the description.

// Nothing has run yet

Placement

side picks where the popover opens, and align lines it up with the trigger.

// Nothing has run yet

Arm delay

armDelay ignores the confirm button for that many milliseconds after the popover opens, so a double click on the trigger can't pass through to it.

// Nothing has run yet

Wait

wait keeps the confirm button disabled for that many milliseconds after the popover opens, with a countdown on it. It works as on Confirm Button.

// Nothing has run yet

Errors

With onConfirmError, a failed onConfirm keeps the popover open and the error goes to your handler. errorLabel replaces the confirm label until the next activation, which tries again.

// Nothing has run yet

Initial focus

Focus starts on the confirm button, so a keyboard user can confirm a quick action with one more key. initialFocus="cancel" starts on Cancel instead, and "none" focuses the popover itself. For an action that deletes data or can't be undone, the WAI-ARIA dialog pattern advises focusing the least destructive action.

// Nothing has run yet

ConfirmPopover

It has no undo, since it closes when the action commits. Use Undo Toast for a way back.

PropTypeDefault
descriptionReactNoderequired
childrenReactElementrequired
onConfirm() => void | Promise<unknown>required
onCancel() => void—
onConfirmError(error: unknown) => void—
titleReactNode—
confirmLabelReactNode"Confirm"
errorLabelReactNode—
cancelLabelReactNode"Cancel"
initialFocus"confirm" | "cancel" | "none""confirm"
variantButton variant"default"
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"
align"start" | "center" | "end""center"
openboolean—
onOpenChange(open: boolean) => void—
gesture"click" | "click-again" | "hold" | "slide""click"
armDelaynumber0
waitnumber0
waitLabel(seconds: number) => ReactNode(s) => `Wait ${s}s`
timeoutnumber3000
durationnumber1200
holdFallback"click-again" | "none""click-again"
announcements{ armed?, hold?, slide?, fallback?, wait?, error? }—
  • The popover is named by title, or by description when there's no title.
  • Focus moves to the confirm button when it opens, or where initialFocus says, and back to the trigger when it closes.
  • While onConfirm is pending, the popover stays open and the confirm button keeps focus.

WCAG

  • 2.2.1 Timing Adjustable: the popover has no time limit. timeout only disarms click-again, and never runs or drops the action.
  • wait isn't a time limit under 2.2.1: it only delays when confirm unlocks, and never ends anything or drops the action.
  • 2.5.2 Pointer Cancellation: gesture="hold" meets it, because releasing early or moving off the button cancels.