Popconfirm

A confirmation attached to the control that triggers it, for a question small enough that a Modal would be too much.

Usage

Popconfirm asks one question next to the control that raised it, and takes the answer as a click. When to use it, and the few decisions it leaves to you.

When to use

Popconfirm asks a question next to the control that raised it, so the page stays where it is. Use it when the action is immediate, single, and fits in one sentence.
  • Deleting or archiving one named thing: a row, a file, a comment.
  • Revoking something that stops working right away: an API key, an invite, a session.
  • Leaving work behind: discarding a draft, closing a form with unsaved changes.
  • A toggle whose off position is costly to undo.
Use something else when:
  • The consequence needs full attention. Use Modal, which takes and keeps focus, closes on Escape and has room to list what will go. Anything that asks the reader to type a name to confirm is a Modal.
  • The reader must answer. Popconfirm closes on a click outside, so dismissing it and canceling are the same gesture. Use Modal.
  • The action can be undone. Let it happen and offer Undo in a message afterwards.
  • The bubble is not a question. Use Popover. Popconfirm needs a title and always shows at least one button.

Writing the question

title is required. A confirmation needs a question.
  • Name the object: “Delete this invoice?” beats “Are you sure?”, which makes the reader remember what they clicked.
  • Put the consequence in description, under the title: “Anything using this key stops working.” Keep it to one sentence. If it needs a paragraph, use a Modal.
  • The title turns bold only when there is a description, so a one-line question stays quiet.
  • Both take a node or a function. The function runs when the bubble opens, so a question that reads live state is built then.

Naming the buttons

okText and cancelText take any node, and fall back to the locale's OK and Cancel.
  • Name the action: Delete, Revoke, Discard. “Yes” only reads next to the question.
  • Say what canceling does where it is not obvious. On a discard prompt, Keep editing beats Cancel.
  • Set labels for the whole app with locale. For every Popconfirm at once, set locale on ConfigProvider instead of each instance.
  • showCancel={false} leaves one button, for a notice the reader only has to acknowledge. The bubble still closes on a click outside, so it cannot force an answer.

The icon

The warning icon comes from your icon pack. Pass icon a node to replace it.
  • Give your own icon me-xs. The default icon has a gap before the title. A glyph you pass does not, so add className="me-xs".
  • Remove the icon with icon={null}. classNames={{ icon: "hidden" }} only hides it and leaves the element in place.
  • The icon does not have to warn. A success icon with no Cancel reads as a confirmation that something worked.

The destructive case

  • okButtonProps and cancelButtonProps take any Button prop, like okButtonProps={{ color: "danger", variant: "solid" }}.
  • Use okButtonProps over okType. okType only takes the older button types. Do not set both.
  • Keep Cancel quieter than OK. A destructive OK beside an equally loud Cancel makes the reader read both.
  • Red is never the whole message. A red button reading “OK” says nothing to someone who cannot see red, so the word has to name the action.

When confirming takes time

Return a Promise from onConfirm. The OK button shows a spinner until the Promise settles, and the bubble closes when it resolves. You do not need to hold any state.
  • A rejected Promise keeps the bubble open. It is also reported as an unhandled rejection, so show the error yourself, for example with a message.
  • To show the error inside the bubble, hold open and okButtonProps.loading in your own state, and close it when the work finishes.

Making the confirmation conditional

disabled turns off the bubble, not the trigger. The child still renders and its own onClick still runs. Use it to skip the question, like when a form has no unsaved changes.
  • Tie it to whatever decides. A form's close button should ask only when the form is dirty, so pass the inverted dirty flag.
  • To stop the action, disable the child instead. A disabled Button inside a Popconfirm cannot be pressed at all.
  • Do not use it as a permission check. The trigger's onClick still runs, unconfirmed.

Where the bubble sits

  • placement takes twelve values and defaults to top: top, left, right, bottom, topLeft, topRight, bottomLeft, bottomRight, leftTop, leftBottom, rightTop, rightBottom.
  • Use one placement per surface. A table of delete buttons whose bubbles open in different directions looks broken.
  • Do not cover the thing the question is about. In a table row, topRight or leftTop keeps the row visible.
  • autoAdjustOverflow is on by default, so a bubble that would leave the screen flips to the other side. Near an edge, it will not be where you placed it.
  • Inside a Modal it opens above the dialog with no z-index to set.

Controlling open

Leave it uncontrolled unless you need otherwise. It opens on click, not hover, and tracks its own open state.
  • defaultOpen opens it on the first render and then lets it manage itself. That is rarely what you want.
  • Passing open takes over closing too. onOpenChange still fires, but the bubble will not close on confirm, cancel or a click outside unless your handler sets open back. Forget that and it stays open after a save.
  • onOpenChange fires on every change, including a click outside. afterOpenChange fires once the animation ends, which is where code that measures the bubble belongs.
  • Leave destroyOnHidden off unless the content is costly. Remounting drops the bubble's state, including a loading flag you set by hand.