Popover

A floating card anchored to a trigger, holding a title, a body and anything interactive. A Tooltip is the version with neither.

Usage

When to use Popover, what each prop is for, and what it leaves to you.

When to use

A floating card anchored to a trigger, with an optional heading, a body, and room for content the reader can use. It is built on Tooltip, so placement and portals work the same way. Unlike Tooltip, it can hold things you click.
  • A detail that is a sentence or two too long for a tooltip.
  • A small form tied to one control: a filter, a rename field, a date range.
  • A preview card on an avatar, a mention or a link, with something the reader may follow.
  • Secondary controls for one row that would crowd it if always visible.
Reach for something else when:
  • The content is only a label. Use a Tooltip, which has the same size, shape and transition props and the same twelve placements.
  • It is a menu of actions. Use a Dropdown, which has the menu roles, arrow keys and active highlight that a card of buttons lacks.
  • It is one question with yes and no. Use a Popconfirm. It already has the buttons, the keyboard handling and the loading state.
  • It takes over the page. Anything the reader has to finish or dismiss first belongs in a Modal. A screen reader hears a popover as a tooltip on its trigger, not as a dialog.

Trigger, and why hover breaks

trigger takes hover (the default), focus, click or contextMenu, or an array of them, like ["hover", "click"]. A ConfigProvider can change the default for a whole subtree.
  • Interactive content needs trigger="click". A hover popover closes when the pointer leaves the trigger, so a button or form inside it is hard to reach.
  • Hover does not reach everyone. There is no hover on touch screens or from the keyboard. Click works for all three.
  • Hover suits read-only content: a definition, a preview, a chart legend. mouseEnterDelay and mouseLeaveDelay default to 0.1 seconds, which gives the pointer time to cross into the bubble.
  • Add focus next to hover to give the keyboard a route to a read-only bubble on a field. It does not help interactive content, because focus leaves the trigger as soon as the reader tabs into the bubble.

Placement and flipping

placement defaults to top and takes twelve values: the four sides, plus eight that align the bubble to an edge of the trigger (topLeft, rightBottom and the rest).
  • It is a preference, not a fixed position. autoAdjustOverflow is on by default, so a bubble near the edge of the screen flips to the other side. You do not need a placement per breakpoint.
  • Open away from where the reader is looking: below a control in a toolbar, above one in a footer bar.
  • Use an edge-aligned placement for a wide bubble on a narrow trigger, where a centered one would hang off both sides.
  • The bubble is themed wherever it renders. Pass getPopupContainer only when the trigger sits in a scrolling panel and the bubble has to scroll with it.

Title and content

On Tooltip, title is the whole bubble. On Popover, title is a heading inside it and content is the body, each with its own classNames key.
  • Either one alone works. With neither, no bubble opens.
  • Keep the title to one line. It has a minimum width, so a short title still gets a readable bubble, and a title that wraps is doing the body's job.
  • Pass a function to content for live values. A node is built when the trigger renders, so a time or a count inside it goes stale. A function runs again each time the popover opens. title takes a function too.
  • Content that has to load belongs in the function form as well, with a skeleton inside the bubble. Do not delay opening.

Controlling open yourself

Popover tracks open itself, starting from defaultOpen (false). Pass open with onOpenChange to take over.
  • Control it when the bubble has to close on something other than the pointer: after a submit, when another popover opens, or on a route change.
  • Control it to add Escape, which the component does not handle. See Dismissal below.
  • onOpenChange is (open: boolean) => void. A handler with a second parameter warns in development.
  • You do not need to control it to log an open. The callback fires either way.

The arrow

arrow takes true, false, or { pointAtCenter: true }, which aims it at the middle of the trigger instead of at the bubble's alignment point.
  • Keep it when several triggers sit close together and the arrow shows which one the bubble belongs to.
  • Drop it when the bubble is wide or its position is already clear.
  • Pair a shape with arrow={false}. The arrow sits outside the bubble and does not follow its corners, so shape="round" and shape="squircle" read best without it, or with a centered placement.

Colors and styling

  • color fills the whole bubble. The kit fills the body as well as the arrow and picks a text color that stays readable on each preset, in both modes. A custom CSS color gets black or white text to match. To change the text yourself, use classNames.title and classNames.content.
  • Style the popup with classNames. The popup renders in a portal, so a descendant selector on the trigger cannot reach it. Use the five keys (root, container, arrow, title, content), and your classes win over the kit's.

Dismissal

How a popover closes depends on how it opened, and Escape never closes it.
  • Hover closes when the pointer leaves, after mouseLeaveDelay. Moving onto the bubble keeps it open.
  • Click closes on a pointer press outside the trigger and the bubble.
  • Escape does nothing. A bubble the keyboard can reach is one it cannot leave. Add it yourself through open and onOpenChange.
  • Close the bubble after the action inside it succeeds. A form that submits and stays open reads as a form that did nothing.
  • destroyOnHidden unmounts the content on close. Use it when a half-typed value should not come back, or a subscription should not outlive the bubble. By default the content stays mounted after the first open.