Popover
A floating panel that appears next to its trigger and holds interactive content. Click-activated, with full keyboard support, positioning, an optional arrow, and compound parts for header, body, and footer.
Playground
Installation
pnpm add @tessinaui/uiUsage
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverHeader,
PopoverTitle,
PopoverDescription,
PopoverBody,
PopoverFooter,
PopoverArrow,
PopoverClose,
} from "@tessinaui/ui";<Popover size="md" rounded="md">
<PopoverTrigger render={<Button>Open</Button>} />
<PopoverContent side="bottom" align="center">
<PopoverHeader showClose>
<PopoverTitle>Popover title</PopoverTitle>
<PopoverDescription>Brief description.</PopoverDescription>
</PopoverHeader>
<PopoverBody>
<p>Any interactive content goes here — forms, lists, buttons…</p>
</PopoverBody>
<PopoverFooter>
<PopoverClose render={<Button variant="ghost">Cancel</Button>} />
<PopoverClose render={<Button>Confirm</Button>} />
</PopoverFooter>
<PopoverArrow />
</PopoverContent>
</Popover>Showcase
When to use Popover vs. Tooltip vs. DropdownMenu vs. Modal
| Use Popover when… | Use Tooltip when… | Use DropdownMenu when… | Use Modal when… |
|---|---|---|---|
| You need interactive content | You just need informational text on hover | The content is a list of menu items | You need to block the rest of the page |
| The trigger is click-activated | The trigger is hover/focus | Users pick one option then dismiss | The decision is critical or blocking |
| The panel can contain a form, list, or buttons | The tip is short and non-interactive | Keyboard arrow navigation between items | Content is large or requires focus |
Anatomy
Popover composes several parts. Only Popover, PopoverTrigger, and PopoverContent are required — everything else is optional.
<Popover>
<PopoverTrigger>…</PopoverTrigger>
<PopoverContent>
<PopoverHeader>
<PopoverTitle />
<PopoverDescription />
</PopoverHeader>
<PopoverBody />
<PopoverFooter />
<PopoverArrow />
</PopoverContent>
</Popover>Size
| Size | Min width | Max width | Text |
|---|---|---|---|
"sm" | 200px | 240px | xs |
"md" (default) | 260px | 320px | sm |
"lg" | 320px | 400px | sm |
"xl" | 400px | 480px | base |
Intent
Applies a semantic border colour to the popup and arrow — useful for warning/error confirmations.
| Value | Border |
|---|---|
"none" (default) | border-border |
"error" | border-error |
"warning" | border-warning |
"success" | border-success |
"info" | border-info |
Rounded
| Value | CSS |
|---|---|
"none" | rounded-none |
"sm" | rounded-md |
"md" (default) | rounded-lg |
"lg" | rounded-xl |
"full" | rounded-3xl |
Positioning
PopoverContent accepts side (top | right | bottom | left), align (start | center | end), sideOffset (px gap from trigger, default 8), and alignOffset (px shift along the side, default 0).
<PopoverContent side="right" align="start" sideOffset={12}>
…
</PopoverContent>Arrow
The <PopoverArrow /> is a rotated square that blends into the popup surface, inheriting the current intent's border colour. It's entirely optional — omit it for a chip-style floating panel.
Backdrop & modal behaviour
By default the popover is non-modal — clicks outside dismiss but the page remains interactive. For destructive confirmations or flows that need focus-trapping, pass modal:
<Popover modal>
…
</Popover>To dim the background, render <PopoverBackdrop /> inside the popover:
<Popover modal>
<PopoverTrigger render={<Button>Delete</Button>} />
<PopoverBackdrop />
<PopoverContent>…</PopoverContent>
</Popover>Controlled
Popover accepts open, defaultOpen, and onOpenChange.
const [open, setOpen] = React.useState(false);
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger render={<Button>Open</Button>} />
<PopoverContent>…</PopoverContent>
</Popover>Close button in header
PopoverHeader accepts showClose — renders a small X button wired to close the popover. Only use when the popover is long-lived or contains interactive content that makes the dismiss affordance non-obvious.
<PopoverHeader showClose>
<PopoverTitle>Rename file</PopoverTitle>
</PopoverHeader>Accessibility
- The trigger and popup are linked via ARIA — screen readers announce the popup's content when it opens.
PopoverTitlemaps toaria-labelledby,PopoverDescriptiontoaria-describedby.- Focus moves to the first focusable element in the popup on open, and returns to the trigger on close.
Escapecloses the popover; outside-click closes it whendismissibleistrue(default).- Use
intent="error"for destructive confirmations so assistive tech surfaces the semantic colour via the rendered border.
RTL
Pass dir="rtl" to the root. PopoverHeader's close button, PopoverFooter actions, and alignment all flip to follow the reading direction.
<Popover dir="rtl">
<PopoverTrigger render={<Button>فتح</Button>} />
<PopoverContent side="bottom" align="start">…</PopoverContent>
</Popover>API Reference
Popover (root)
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "xl" | "md" | Min/max width + text size |
rounded | "none" | "sm" | "md" | "lg" | "full" | "md" | Corner radius of popup |
intent | "none" | "error" | "warning" | "success" | "info" | "none" | Semantic border colour |
dir | "ltr" | "rtl" | "ltr" | Reading direction |
open | boolean | — | Controlled open state |
defaultOpen | boolean | false | Uncontrolled initial state |
onOpenChange | (open, event) => void | — | Fired when open state changes |
modal | boolean | false | Trap focus and block page interaction |
delay | number | — | Hover-open delay (when the trigger is hoverable) |
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | "top" | "right" | "bottom" | "left" | "bottom" | Which side of the trigger to position on |
align | "start" | "center" | "end" | "center" | Alignment along the side axis |
sideOffset | number | 8 | Distance in px from the trigger |
alignOffset | number | 0 | Shift along the side axis |
portal | boolean | true | Render inside a portal |
container | HTMLElement | null | document.body | Portal target |
size, rounded, intent | — | inherited | Override the root's variant |
PopoverHeader
| Prop | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | — | Leading icon rendered beside the title |
showClose | boolean | false | Render the built-in X close button |
PopoverTrigger, PopoverClose
Render a <button>. Use render={<CustomElement />} to swap in a Button or any element. Standard Base UI render-prop pattern.
Sub-components
PopoverTitle, PopoverDescription, PopoverBody, PopoverFooter, PopoverBackdrop, PopoverArrow — all accept className and any HTML props.
Notes
- Built on
@base-ui/react/popover— the same headless primitive used by the design system for tooltips and menus. - Enter/exit animations use Base UI's
data-[starting-style]anddata-[ending-style]plus position-awaredata-[side=*]attributes, so the popup "lifts off" from whichever side it opens on. - The arrow is drawn as a single rotated square — no SVG — so it inherits the border colour automatically when
intentchanges.
Command
Command palette / ⌘K menu — filterable, keyboard-driven action launcher with inline and modal modes, grouped items, descriptions, breadcrumbs, shortcuts, and footer key hints.
HoverCard
A floating card that appears when a trigger is hovered or focused. Use for lightweight previews — user profiles, link previews, citations, or footnote definitions. Built on Base UI's preview-card primitive.