Skip to content

Popover

Stable

A floating panel anchored to a trigger for small, interactive content — settings, quick forms and pickers — without blocking the page.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/popover.json

Usage

import {
  Popover,
  PopoverContent,
  PopoverDescription,
  PopoverHeader,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
<Popover>
  <PopoverTrigger render={<Button variant="outline" />}>Dimensions</PopoverTrigger>
  <PopoverContent>
    <PopoverHeader>
      <PopoverTitle>Dimensions</PopoverTitle>
      <PopoverDescription>Set the size of the selected frame.</PopoverDescription>
    </PopoverHeader>
    {/* … */}
  </PopoverContent>
</Popover>

The popover is positioned by Floating UI: it opens below its trigger, flips to the other side when there isn't room, and stays inside the viewport. It enters from the trigger's direction — the popup-motion choreography shared by every prfct floating surface.

Examples

Quick input

A popover can hold a small form. Control it with open and onOpenChange so it closes after a successful submit, and confirm with a Toast.

PopoverHeader, PopoverTitle and PopoverDescription give a popover a title that names it for assistive technology. Use them whenever the content has more than one control.

Side

side places the popover on the top, right, bottom (default) or left of its trigger. If the preferred side doesn't fit, it flips.

Alignment

align lines the popover up with the start, center (default) or end of the trigger. Align to the start for menus that read left to right, and to the end for triggers near the right edge.

Guidelines

When to use

  • For controls that act on one element: formatting, dimensions, filters for a single column.
  • For quick inputs that don't need a full dialog: a note, a rename, a date.
  • For pickers: colors, emoji, dates.

When not to use

  • To show a plain text label — use a Tooltip.
  • To preview a link on hover — use a Hover Card.
  • For a list of actions — use a Dropdown Menu, which gives you arrow-key navigation and typeahead.
  • For tasks that need focus and a clear finish — use a Dialog.

Keep it small

A popover is a glance, not a destination. If its content needs to scroll, needs more than a few controls, or needs a Save button, it has outgrown the popover. Keep it under ~20rem wide and a few rows tall.

Reading
Wrap lines
Line numbers
Do.A handful of controls that apply immediately.
Edit profile
Don’t.A form that scrolls and needs saving belongs in a dialog or a sheet.

Accessibility

The trigger is a button with aria-expanded and aria-haspopup="dialog"; the popup is a non-modal dialog, labeled by PopoverTitle when present.

KeyBehavior
EnterSpace
On the trigger: toggles the popover.
Tab
Moves focus into the popover, then through its controls and on to the rest of the page.
Esc
Closes the popover and returns focus to the trigger.
  • Non-modal by default. The page stays interactive; clicking outside or moving focus away closes the popover. Pass modal to trap focus and lock scroll, or modal="trap-focus" to trap focus only.
  • Name it. Use PopoverTitle for any popover with controls. Without a visible title, add an aria-label to PopoverContent.
  • Hover opening is opt-in. openOnHover on the trigger also opens it on hover, but it always opens on click and keyboard too — never make hover the only way in.

API reference

Popover

The root. Doesn't render an element. Accepts every prop of Base UI Popover.Root.

PropTypeDefault
open

Whether the popover is open. Use with onOpenChange to control it.

booleanNo default
defaultOpen

Whether it is open initially, when uncontrolled.

booleanfalse
onOpenChange

Called when it opens or closes.

(open: boolean, details) => voidNo default
modal

true traps focus, locks scroll and blocks outside interaction.

boolean | "trap-focus"false
actionsRef

Imperative handle to close or unmount the popover.

RefObject<{ close, unmount }>No default

PopoverTrigger

Toggles the popover. Renders a <button>; use render to render a prfct Button.

PropTypeDefault
openOnHover

Also opens the popover when the trigger is hovered.

booleanfalse
delay

Hover delay before opening, in ms. Requires openOnHover.

number300
closeDelay

Delay before closing a popover opened on hover, in ms.

number0

PopoverContent

Renders the portal, positioner and popup.

PropTypeDefault
side

Preferred side of the trigger. Flips when it doesn't fit.

"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end""bottom"
sideOffset

Distance from the trigger, in px.

number6
align

Alignment against the trigger.

"start" | "center" | "end""center"
alignOffset

Offset along the alignment axis, in px.

number0
initialFocus

Element to focus when it opens.

boolean | RefObject | (interaction) => HTMLElement | booleanNo default
finalFocus

Element to focus when it closes.

boolean | RefObject | (interaction) => HTMLElement | booleanNo default
className

Merged with the popup classes. The default width is w-72.

stringNo default

PopoverHeader, PopoverTitle, PopoverDescription

PopoverHeader stacks the title and description. PopoverTitle renders an <h2> that labels the popover; PopoverDescription renders a <p> that describes it.