Popover

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

title
description
side
align
<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.json

Usage

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

PropTypeDefaultDescription
openboolean–Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open, eventDetails) => void–Called when the popover opens or closes.
modalbooleanfalseWhen true, traps focus and blocks interaction outside the popup.

PopoverContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left" | "inline-start" | "inline-end""bottom"Which side of the trigger the popup opens on.
sideOffsetnumber4Gap between the trigger and the popup.
align"start" | "center" | "end""center"Alignment along the trigger edge.
alignOffsetnumber0Extra offset along the alignment axis.
ComponentDescription
PopoverTriggerOpens the popup (data-slot="popover-trigger"). Use the render prop to compose it onto your own element, e.g. a Button.
PopoverContentPortalled 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.
PopoverHeaderTight column for title and description (data-slot="popover-header").
PopoverTitlePopup heading (data-slot="popover-title"). Wired to the popup via aria-labelledby by Base UI.
PopoverDescriptionSupporting 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.

Search documentation

Search the component docs