A popover is an elevated card anchored to something on the page, holding a short passage and a control or two. It takes focus, because there is something in it to use, and it leaves the rest of the page live, because it is not asking the reader to stop.
Anatomy
Section titled “Anatomy”-
Trigger △ the element that opens the popover. Supply an anchor as well and the surface positions against that instead, while the trigger keeps the click.Passed 2 minutes ago.Last check
Passed 2 minutes ago.Last check
-
Container (when open) the elevated card: shape, elevation and shadow. It takes focus, because a popover is a surface the reader can act in.Passed 2 minutes ago.Last check
Passed 2 minutes ago.Last check
-
Heading (optional, when open) names what the surface is about, in one line.Passed 2 minutes ago.Last check
Passed 2 minutes ago.Last check
-
Content (when open) the body: a short passage and at most one or two actions. Anything that scrolls belongs in a different container.Passed 2 minutes ago.Last check
Passed 2 minutes ago.Last check
When to use
Section titled “When to use”Three components open a surface over the page, and what separates them is how much of the page they claim.
A Tooltip describes the thing you are pointing at. It
is role="tooltip", it never takes focus, and nothing inside it can be
operated — a control in a tooltip is unreachable from a keyboard, so the moment
the content has a button in it, it is not a tooltip.
A popover is a surface the reader acts in. It takes focus and holds controls, and the page behind stays scrollable and clickable.
A Modal takes the page over. The background goes inert, and the reader answers before anything else can happen.
So the two questions are whether there is something to do, and whether the rest of the page must wait. Nothing to do makes it a tooltip; the page having to wait makes it a modal; a workflow with more than one step is always a modal, because a popover has no room and closes itself. For a list of actions rather than a passage of content, use a Dropdown Menu.
Behavior
Section titled “Behavior”Open the popover from a click, never a hover
Section titled “Open the popover from a click, never a hover”The trigger opens on click and only on click — the underlying Radix trigger binds no hover or focus handler. Keyboard readers get in because the trigger is a button and Enter fires its click; there is nothing to configure.
That is deliberate rather than missing. A surface that takes focus cannot be opened by hovering, because a pointer is the one input that has no way to hand focus over, and a keyboard is the one input that cannot hover. If the content is short enough that hovering feels right, it is a tooltip.
trigger and anchor can be two different elements: the trigger keeps the
click, and the popover positions itself against the anchor instead. Use that
when the thing being described is not the thing being clicked — an icon beside a
row that should open a card aligned to the whole row.
portal defaults to true, so the surface renders at the end of the document
rather than beside the trigger. Where it appears and where it lives are separate
questions; turn the portal off when the popover has to inherit something from its
place in the tree, and expect an ancestor with overflow: hidden to clip it when
you do.
Radix’s root props reach the popover, including the open state
Section titled “Radix’s root props reach the popover, including the open state”open, defaultOpen and onOpenChange are all accepted — they are Radix’s own
root props, which is what the props table’s closing line means when it says the
component takes everything on PopoverPrimitive.PopoverProps. They are not
listed in the table above it, so they are easy to conclude are absent. They are
not, and triggerProps is not where the open state lives; that prop only reaches
the trigger button.
modal is the same kind of inherited prop, and it defaults to false. That
default is the behavior described above: no focus trap, the page still scrolls,
clicks outside land where they were aimed and close the popover on the way.
Setting it to true gives you a modal without a modal’s overlay, which leaves
the reader trapped in a surface that does not look like it holds them.
Pair the close button with a heading
Section titled “Pair the close button with a heading”A popover closes on Esc and on a click outside, and that is usually enough. For
an explicit exit, contentProps={{ showCloseButton: true }} draws an X — but
only supply it alongside heading, because with a heading the X gets its own
row in the header, and without one it is positioned absolutely over the top
right of the content.
Its accessible name is fixed at “Close” and nothing can be forwarded to it, so it cannot be renamed or targeted in a test. That is logged upstream; until it changes, the close button is an affordance you can turn on and cannot adjust.
Accessibility
Section titled “Accessibility”Name the popover, because nothing else does
Section titled “Name the popover, because nothing else does”Measured on this page’s own specimen: the surface is role="dialog" with no
aria-label, no aria-labelledby and no aria-describedby. heading renders
a bold paragraph and is not wired to the dialog, so a popover with a heading is
still announced as an unnamed dialog.
Pass the name yourself — contentProps={{ 'aria-label': 'Verification details' }}
reaches the dialog element, because contentProps spreads onto it. The trigger
needs nothing: it already carries aria-haspopup="dialog", aria-expanded and
aria-controls, and focus moves into the surface on open and returns to the
trigger on close.
Content
Section titled “Content”Hold one short passage and at most two actions
Section titled “Hold one short passage and at most two actions”The popover is gone the moment the reader clicks away, and nothing in it is recoverable. Anything they need to hold onto while doing something else belongs on the page, and anything they need to compare against something else belongs next to it.
contentProps will take a maxHeight and scroll inside it, which is the
component admitting more content than it should be asked to hold. Reach for it
for a list that is occasionally long, not as permission for a form: a tall
popover covers the thing it is anchored to, which is the only context the reader
has for reading it.
Capitalization, punctuation and the rules that apply to every label are on Writing.
| Name | Type |
|---|---|
trigger |
Component to use as trigger. Radix UI trigger component props will be merged with this element. |
triggerProps |
Props to apply to Popover.Trigger within |
anchor |
An optional element to position the Popover.Content against. If this part is not used, the content will position alongside the trigger. |
contentProps |
Props to apply to Popover.Card within |
portal |
Render content within a portal |
portalProps |
Props applied to Radix UI Portal component |
PopoverPrimitive.PopoverProps.Was this page helpful?