v1.0

Popover

Anchored floating panel for contextual content next to a trigger, without blocking the page.

Pro

Description

Popover is a compound component built on Radix Popover. It renders a portal-anchored floating panel next to whatever opened it, composed from a root plus sub-parts: PopoverTrigger, PopoverAnchor, PopoverContent, PopoverArrow, PopoverClose, PopoverImage, PopoverBody, PopoverHeader, PopoverTitle, PopoverDescription, PopoverFooter, PopoverActions, and PopoverActionText.

PopoverContent owns the size and hands it to every child through context, so setting size once there cascades to the width, radius, shadow, and the title, description, body, and footer text. Reach for it for contextual mini-forms, rich tooltips, anchored option panels, and profile hovercards, anything that adds detail next to a trigger without interrupting the page. For a blocking interaction that traps focus, use Modal; for a plain text hint, use Tooltip; for a list of actions, use Dropdown Menu.

Popover is a Create UI Pro component. With a Pro seat, npx @create-ui/cli add popover installs it. The previews below are marked with a Pro badge.

Installation

pnpm dlx @create-ui/cli add popover

Anatomy

PopoverContent renders the portal itself, so it sits directly under the trigger (or anchor). Put the image above PopoverBody, and the header, footer inside it.

<Popover>
  <PopoverTrigger />
  <PopoverAnchor />
  <PopoverContent>
    <PopoverImage />
    <PopoverBody>
      <PopoverHeader>
        <PopoverTitle />
        <PopoverDescription />
      </PopoverHeader>
      <PopoverFooter>
        <PopoverActionText />
        <PopoverActions />
      </PopoverFooter>
    </PopoverBody>
  </PopoverContent>
</Popover>

PopoverAnchor is optional: omit it and the panel anchors to PopoverTrigger. Include it only when the panel should point at a different element than the one that opens it.

Usage

import {
  Popover,
  PopoverActions,
  PopoverActionText,
  PopoverAnchor,
  PopoverArrow,
  PopoverBody,
  PopoverClose,
  PopoverContent,
  PopoverDescription,
  PopoverFooter,
  PopoverHeader,
  PopoverImage,
  PopoverTitle,
  PopoverTrigger,
} from "@/components/ui/popover"
<Popover>
  <PopoverTrigger asChild>
    <Button>Open</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverTitle>Details</PopoverTitle>
  </PopoverContent>
</Popover>

Examples

Every example below is a Pro preview (marked with a Pro badge). Each renders a trigger button; open it to see the panel.

Sizes

size sets the width, radius, and shadow on PopoverContent, and cascades to every child's spacing and type scale. sm, md, and lg cover most contextual panels; sm is the default.

Pro

Placement

side sets which edge of the trigger the panel opens from: top, right, bottom, or left. bottom is the default.

Pro

Alignment

align sets how the panel lines up along that edge: start, center, or end. For start and end, the offset auto-derives from size so the panel doesn't overhang the trigger.

Pro

With Image

PopoverImage renders above PopoverBody and clips its child to the content's radius. Use it for release notes, media previews, or anything with a banner image.

Pro

With Close Button

showClose renders a built-in top-right soft-pill close button, no extra wiring needed. Leave it off (the default) when the panel closes on outside click, Escape, or a PopoverClose you place yourself.

Pro

PopoverFooter lays out a row: PopoverActionText is the muted status text on the left, PopoverActions groups the buttons on the right. Wrap a dismiss control in PopoverClose asChild to close on click.

Pro

No Arrow

showArrow={false} drops the little tail pointing at the trigger and nudges sideOffset out to compensate. Use this for panels that shouldn't read as "attached" to a specific point.

Pro

Anchor

Render PopoverAnchor around an element other than PopoverTrigger to position the panel against it. The trigger inside still controls open state; only the positioning target moves.

Pro
Anchor

Controlled

Pass open and onOpenChange on the root to drive the panel from your own state, useful when something outside the trigger (a keyboard shortcut, another control) needs to open or close it.

Pro

Accessibility

Popover is built on Radix Popover, which provides dismiss-on-outside-click, Escape-to-close, and focus return to the trigger on close.

KeyDescription
EnterOpens the panel when trigger focused.
SpaceOpens the panel when trigger focused.
EscapeCloses the panel and returns focus to the trigger.
TabMoves focus to the next focusable element inside the panel, or out of it at the end.

ARIA notes:

  • PopoverTrigger sets aria-expanded and aria-haspopup on the underlying trigger element automatically.
  • PopoverTitle and PopoverDescription are plain styled div/p elements, not Radix-labelled primitives, so nothing is auto-announced. If the panel needs a programmatic label, add aria-label or aria-labelledby yourself.
  • When showClose is true, the rendered CloseButton provides its own aria-label.
  • Opening does not trap focus or lock scroll (unlike Modal); closing returns focus to the trigger.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):

<PopoverContent className="w-80">{/* ... */}</PopoverContent>

Data slots and attributes: the components set these for CSS targeting:

  • data-slot="popover" on the root.
  • data-slot="popover-trigger" on the trigger.
  • data-slot="popover-anchor" on PopoverAnchor.
  • data-slot="popover-arrow" on the arrow.
  • data-slot="popover-close" on PopoverClose.
  • data-slot="popover-content" on the panel, with Radix's data-state and data-side for open/close and placement animations. size is not exposed as a data-* attribute; it only drives styling through PopoverSizeContext.
  • data-slot="popover-image" on the image wrapper.
  • data-slot="popover-body" on the content column.
  • data-slot="popover-header" on the header.
  • data-slot="popover-title" and data-slot="popover-description" on the text.
  • data-slot="popover-footer" on the footer row.
  • data-slot="popover-actions" on the button cluster and data-slot="popover-action-text" on the status text.

Target a specific state in CSS:

[data-slot="popover-content"][data-state="open"] {
  /* ... */
}
  • Modal: use this for a blocking interaction that traps focus, not anchored contextual content.
  • Tooltip: use this for a passive text hint, not interactive content.
  • Dropdown Menu: use this for a list of actions, not free-form content.
  • Close Button: the control PopoverClose renders when showClose is true.

API Reference

Popover

Root that forwards to Radix Popover.Root. Extends React.ComponentProps<typeof PopoverPrimitive.Root>, so open, defaultOpen, onOpenChange, and modal are accepted.

Props

PropTypeDefaultDescription
openboolean-Controlled open state.
defaultOpenbooleanfalseUncontrolled initial open state.
onOpenChange(open: boolean) => void-Fires when the open state changes.
childrenReact.ReactNode-The trigger, anchor, and content.

PopoverTrigger

The element that opens the panel. Wraps Radix Popover.Trigger; use asChild to render your own Button. Renders <button data-slot="popover-trigger">.

PopoverAnchor

Optional positioning target when the panel should point at something other than the trigger. Wraps Radix Popover.Anchor. Extends React.ComponentProps<typeof PopoverPrimitive.Anchor>.

PopoverContent

The floating panel. Renders the Radix portal, then your children. Extends React.ComponentProps<typeof PopoverPrimitive.Content> (minus children, className, side, align, sideOffset, alignOffset, which are redeclared below).

Props

PropTypeDefaultDescription
size"sm" "md" "lg""sm"Width, radius, and shadow; cascades to every child via context.
side"top" "bottom" "left" "right""bottom"Which edge of the trigger the panel opens from.
align"start" "center" "end""center"Alignment along that edge.
sideOffsetnumber2.5Distance from the trigger; widened automatically when showArrow is false.
alignOffsetnumber-Overrides the size-derived offset for start/end alignment.
showArrowbooleantrueRenders the tail pointing at the trigger.
showClosebooleanfalseRenders a built-in top-right soft-pill close button.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-PopoverImage, PopoverBody, and any composed sub-parts.

PopoverArrow

Decorative tail pointing at the trigger. Rendered automatically by PopoverContent when showArrow is true; you don't render it directly.

PopoverClose

Wraps Radix Popover.Close and renders a CloseButton via asChild. Use your own asChild child (a footer Button) to make any control dismiss the panel.

PopoverImage

Banner slot rendered above PopoverBody. Clips its child image to the content's radius. Extends React.ComponentProps<"div">.

PopoverBody

Content column below the image. Extends React.ComponentProps<"div"> (padding and gap come from context).

PopoverHeader

Stacks the title and description. Extends React.ComponentProps<"div">.

PopoverTitle / PopoverDescription

Plain styled div (title) and p (description). Not Radix-labelled primitives, so add aria-label yourself if the panel needs a programmatic label.

PopoverFooter

Action row below the body. Extends React.ComponentProps<"div">.

PopoverActions

Trailing button cluster inside PopoverFooter. Extends React.ComponentProps<"div">.

PopoverActionText

Muted, left-aligned status text inside PopoverFooter. Extends React.ComponentProps<"p">.

Types

type PopoverSize = "sm" | "md" | "lg"