Popover
A raised panel anchored to a trigger. Click to open, Escape or a click outside to close.
A small panel that opens beside the control that asked for it, for a few filters, a short form, or a picker that does not need a whole screen. It sits on the Radix popover, which positions it against the trigger, flips it when it would run off the window, and leaves the rest of the page usable. Combobox and Date picker are built on it.
Usage
import { Button } from '@/components/ui/button';
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from '@/components/ui/popover';
<Popover>
<PopoverTrigger asChild>
<Button>Filters</Button>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Filters</PopoverTitle>
<PopoverDescription>Applied to this view only.</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>PopoverContent is 288px wide with 16px of padding and a 10px gap between its children, on
the raised surface with a light ring and a shadow. Pass className to change the width,
like w-56. PopoverHeader, PopoverTitle, and PopoverDescription are optional layout
slots, and PopoverClose closes the panel from inside it.
Where it opens
side picks the edge of the trigger it opens from, align picks start, center, or
end along that edge, and sideOffset sets the gap, 4px by default. The panel flips to the
other side when there is no room, as on the Radix popover, and collisionPadding keeps it
off the window edge.
To open the panel against something other than the trigger, put PopoverAnchor around that
element. The trigger still opens it, and the panel positions to the anchor.
Modal or not
The popover is not modal by default, so the page behind it stays scrollable and clickable.
Clicking elsewhere closes it, and so does Escape or moving focus out of it, and a click on
the trigger toggles it. modal on Popover makes it hold focus the way a dialog does: focus
cannot be moved out of the panel, the page behind it is hidden from assistive tech and
cannot scroll, and a click outside closes it without reaching the page. Reach for modal
when the panel is a task the user should finish, and for a
Dialog when it deserves a scrim, which a popover never
has.
Motion
The panel fades in and out and does not slide or scale, so it does not fight the Radix positioning, which moves it with a transform. Under reduced motion it appears and disappears without animating.
Props
Popover, PopoverTrigger, PopoverAnchor, and PopoverClose are the Radix popover parts,
with open, defaultOpen, onOpenChange, and modal as on the Radix popover.
PopoverContent takes the Radix content props, with two defaults changed.
| Prop | Type | Default | What it does |
|---|---|---|---|
align | "start" | "center" | "end" | "center" | Where the panel lines up along the trigger's edge. |
sideOffset | number | 4 | The gap in pixels between the trigger and the panel. |
side, alignOffset, collisionPadding, onOpenAutoFocus, onCloseAutoFocus, and the
other Radix content props pass through.
Accessibility
- The trigger is a native button, so Enter and Space open it. It carries
aria-haspopup="dialog",aria-expanded, and, while open,aria-controlspointing at the panel. - The panel has
role="dialog".PopoverTitleandPopoverDescriptionare a plain heading and paragraph and are not wired to it, so the panel has no accessible name until you setaria-labeloraria-labelledbyonPopoverContent. - Focus moves into the panel on open, to the first focusable element in it, and goes back to the trigger on close, unless the user closed it by clicking elsewhere, in which case focus stays where the click put it.
- Escape closes it. Tab and Shift+Tab loop within the panel, from the last element to the
first, so the keyboard does not walk out of it. That is the only trapping a popover does by
default: it does not hide the page or lock scrolling unless
modalis on, and a click or a programmatic focus outside closes it. - The panel carries a transparent outline that the system paints in forced colors, so its edge stays visible in Windows high contrast. The ring and shadow are removed there.
- Nothing in the popover sets a touch target size, so the controls you put in it need their own.
- Under reduced motion it opens and closes without the fade.
When to use something else
- Hover card for a preview that opens on hover and holds nothing the user has to act on. A popover opens on click and its content can be interactive.
- Tooltip for a one-line label on an icon or a control. A tooltip names something and is never interactive.
- Dropdown menu for a list of actions, since it brings arrow-key navigation, typeahead, and a menu role that a popover does not.
- Dialog or Sheet when the content needs the user's full attention or more room than a panel beside a control can give.
Install
shadcn add @gesso/popover