The float surface that menus, listboxes and small panels open in, flipped to fit the screen.
Free content
A popover opens beside its trigger, with the float fill, frame, surface corner and float shadow. Menus and lists of options open in it, and it also holds free content, such as a filter panel whose choices apply as they change. isPadded pads free content 24px. It is off by default, because a list or a menu pads itself.
Example
Example
import { Button, Checkbox, CheckboxGroup, DialogTrigger, Popover, Switch } from '@snapshot-ui/react';
import type { ReactElement } from 'react';
export default function PopoverBasic(): ReactElement {
return (
<DialogTrigger>
<Button variant="secondary">Filter the projects</Button>
<Popover isPadded>
<div style={{ display: 'grid', gap: 24 }}>
<CheckboxGroup label="Status" defaultValue={['active']}>
<Checkbox value="active">Active</Checkbox>
<Checkbox value="paused">Paused</Checkbox>
<Checkbox value="archived">Archived</Checkbox>
</CheckboxGroup>
<Switch defaultSelected>Only projects I own</Switch>
</div>
</Popover>
</DialogTrigger>
);
}Matching the trigger
matchTriggerWidth makes the trigger's width the popover's minimum, so a panel under a wide trigger lines up with both edges. Wider content still widens the popover.
Example
Example
import { Button, DialogTrigger, Popover, Radio, RadioGroup } from '@snapshot-ui/react';
import type { ReactElement } from 'react';
export default function PopoverMatchWidth(): ReactElement {
return (
<DialogTrigger>
<Button variant="secondary" style={{ width: '100%', maxWidth: '15rem' }}>
Sort the projects
</Button>
<Popover isPadded matchTriggerWidth>
<RadioGroup label="Sort by" defaultValue="recent">
<Radio value="recent">Most recent</Radio>
<Radio value="name">Name</Radio>
<Radio value="owner">Owner</Radio>
</RadioGroup>
</Popover>
</DialogTrigger>
);
}Usage
Wrap free content and its button in a DialogTrigger. Or control the popover with isOpen and onOpenChange, and point triggerRef at the element it opens from. A menu, a select or a combo box brings its own trigger, so do not wrap it in a DialogTrigger. Popover is a client component that a server component can render, as the examples on this page do.
It opens 8px under its trigger at the start edge. placement and offset change that: placement="top end" opens it above the trigger at the end edge.
- Fitting: with no room on its side, it flips to the other side, and it shifts along the trigger to stay 12px inside the window.
data-placement: names the side the popover landed on.- Size: never wider than the screen less 2rem. Taller content scrolls inside it.
- Right to left:
startandendfollow the writing direction. - No arrow: the gap and the shadow tie it to its trigger.
A popover takes the theme, shape and density of its trigger, so one opened from a dark card is dark on a light page. See Theming.
Props
ref and className go on the popover element, the one that takes focus. className is merged after the library's classes. triggerRef is the element it opens against. A trigger component hands it over. Outside one, a popover opened with isOpen or defaultOpen needs it, or it throws an error that names triggerRef. The rest of React Aria's popover props pass through.
children ((values: PopoverRenderProps & { defaultChildren: ReactNode | undefined; }) => ReactNode) | ReactNode | The content: free content, padded with |
|---|---|
className string | ((values: PopoverRenderProps & { defaultClassName: string | undefined; }) => string) | Goes on the popover element, merged after the library's classes. |
defaultOpen boolean | Whether the overlay is open by default (uncontrolled). |
isOpen boolean | Whether the overlay is open by default (controlled). |
isPadded boolean | Pads free content 24px. Lists and menus pad themselves, so it is off by default. Default: false |
matchTriggerWidth boolean | Makes the trigger's width the popover's minimum. Default: false |
offset number | The gap to the trigger. Default: 8 |
onOpenChange ((isOpen: boolean) => void) | Handler that is called when the overlay's open state changes. |
placement Placement | Default: 'bottom start' |
triggerRef RefObject<Element | null> | The ref for the element which the popover positions itself with respect to. When used within a trigger component such as DialogTrigger, MenuTrigger, Select, etc., this is set automatically. It is only required when used standalone. |
Accessibility
Opened from a DialogTrigger, the popover is a dialog named by its trigger. Focus moves into it, and Tab cycles through its controls without reaching the page. Escape or a press outside closes it and returns focus to the trigger. While it is open, the page behind is hidden from assistive technology and does not scroll.
| Escape | Closes the popover. |
|---|---|
| Tab | Cycles through the controls inside. |
Design rules
What keeps popovers in the system.
Keep a popover to one small task, such as filtering a list.
Pad free content with isPadded.
Let a list or a menu pad itself.
Never use a popover to ask a question or for a form that saves. That is a dialog.
Never make a popover wider than the screen less 2rem.
Never add an arrow. The gap and the shadow tie it to its trigger.