Popover
A floating card anchored to a trigger, built on the Base UI popover. Composes headers, forms, or toolbars with side and alignment controls.
Playground
<Popover>
<PopoverTrigger render={<Button variant="outline">Open</Button>} />
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Popover title</PopoverTitle>
<PopoverDescription>A short description of this popover.</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>Preview
Wrap a trigger and content in Popover. Use the render prop on PopoverTrigger to render your own element (like a Button) as the trigger, and compose the popup from PopoverHeader, PopoverTitle and PopoverDescription.
<Popover>
<PopoverTrigger render={<Button variant="outline">Open</Button>} />
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Popover title</PopoverTitle>
<PopoverDescription>
A short description of what this popover is for.
</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>Installation
npx shadcn@latest add https://espresso-base-ui.vercel.app/r/popover.jsonUsage
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverHeader,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover"<Popover>
<PopoverTrigger render={<Button variant="outline">Open</Button>} />
<PopoverContent side="bottom" align="center">
<PopoverHeader>
<PopoverTitle>Title</PopoverTitle>
<PopoverDescription>Description</PopoverDescription>
</PopoverHeader>
</PopoverContent>
</Popover>With form
The content is a plain flex column (gap-2.5), so form fields drop straight in below the header.
<Popover>
<PopoverTrigger
render={
<Button variant="outline">
<Settings2 />
Dimensions
</Button>
}
/>
<PopoverContent>
<PopoverHeader>
<PopoverTitle>Dimensions</PopoverTitle>
<PopoverDescription>Set the dimensions for the layer.</PopoverDescription>
</PopoverHeader>
<Field>
<FieldLabel>Width</FieldLabel>
<Input defaultValue="100%" />
</Field>
<Field>
<FieldLabel>Height</FieldLabel>
<Input defaultValue="25px" />
</Field>
</PopoverContent>
</Popover>Positioning
PopoverContent forwards side, sideOffset, align and alignOffset to the positioner. The popup slides in from the chosen side.
<Popover>
<PopoverTrigger render={<Button variant="outline">Top</Button>} />
<PopoverContent side="top">...</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger render={<Button variant="outline">Start</Button>} />
<PopoverContent align="start">...</PopoverContent>
</Popover>API reference
Popover wraps the Base UI popover root and shares open state with its children. PopoverContent renders the portal, positioner and popup in one component; the remaining parts are styled wrappers that accept className and their underlying element props.
Popover
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | – | Controlled open state. |
| defaultOpen | boolean | false | Initial open state when uncontrolled. |
| onOpenChange | (open, eventDetails) => void | – | Called when the popover opens or closes. |
| modal | boolean | false | When true, traps focus and blocks interaction outside the popup. |
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
| side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "bottom" | Which side of the trigger the popup opens on. |
| sideOffset | number | 4 | Gap between the trigger and the popup. |
| align | "start" | "center" | "end" | "center" | Alignment along the trigger edge. |
| alignOffset | number | 0 | Extra offset along the alignment axis. |
| Component | Description |
|---|---|
| PopoverTrigger | Opens the popup (data-slot="popover-trigger"). Use the render prop to compose it onto your own element, e.g. a Button. |
| PopoverContent | Portalled popup card (data-slot="popover-content") – w-72 bg-card surface with fade/zoom/slide animations driven by data-open, data-closed and data-side. |
| PopoverHeader | Tight column for title and description (data-slot="popover-header"). |
| PopoverTitle | Popup heading (data-slot="popover-title"). Wired to the popup via aria-labelledby by Base UI. |
| PopoverDescription | Supporting copy (data-slot="popover-description"). Wired to the popup via aria-describedby by Base UI. |
Accessibility & styling hooks
Base UI handles the accessibility contract: the trigger gets aria-haspopup/aria-expanded, the popup is labelled by PopoverTitle and described by PopoverDescription, and Escape or an outside click dismisses it. For styling, every part exposes a data-slot attribute, and the popup reflects its state and placement as data-open/data-closed and data-side – the built-in animations key off these.