Popover
Displays rich content in a portal, triggered by a button.
Installation
bash
npx shadcn@latest add @redpanda/popoverbash
pnpm dlx shadcn@latest add @redpanda/popoverbash
yarn dlx shadcn@latest add @redpanda/popoverbash
bunx shadcn@latest add @redpanda/popoverbash
nubx shadcn@latest add @redpanda/popoverbash
aube dlx shadcn@latest add @redpanda/popoverPositioning
Controlled
When to use
The Popover component displays rich content in a positioned overlay triggered by user interaction. Use this decision tree to determine when it’s appropriate:
Usage
tsx
<Popover> <PopoverTrigger>Open Popover</PopoverTrigger> <PopoverContent>Popover Content</PopoverContent></Popover>Examples
Date Picker Example
Props
Content padding
Use PopoverContent padding="none" for a calendar or another component that owns
its own inset. padding="sm" supplies 8 pixels and the default padding="md"
supplies 16 pixels. Keep placement and width classes on the popup; do not override
its padding with utilities.
Keyboard interactions
| Key | Action |
|---|---|
Enter / Space on PopoverTrigger |
Toggles the popover open/closed. |
Tab / Shift+Tab |
Cycles focus between focusable elements inside the popover. |
Escape |
Closes the popover and restores focus to the trigger. |
Click outside PopoverContent |
Closes the popover. |
Accessibility
PopoverTriggerexposesaria-expanded/aria-controlsfor the screen-reader state of the popover.PopoverContentis focusable and traps focus while open — pressingTabpast the last focusable control wraps back to the first.- The trigger must be a real button (or another interactive element via the
renderprop). Static<div>triggers lose focus behaviour. onOpenChangeuses Base UI’s native(open, eventDetails) => voidsignature.
Controlled vs uncontrolled
Uncontrolled — the popover owns open state:
tsx
<Popover onOpenChange={(open) => console.log('open:', open)}> <PopoverTrigger>Open</PopoverTrigger> <PopoverContent>{/* ... */}</PopoverContent></Popover>Controlled — drive open from parent state (e.g. to open the popover programmatically or close it after a form
submission):
tsx
const [open, setOpen] = useState(false);
<Popover open={open} onOpenChange={setOpen}> <PopoverTrigger render={ <Button onClick={() => undefined} variant="outline"> Open </Button> } /> <PopoverContent> <Button onClick={() => setOpen(false)}>Done</Button> </PopoverContent></Popover>;