Components

Portalled popover surface for small contextual content anchored to a trigger.

Install

npx shadcn@latest add @alix/popover

Preview

Usage

<Popover>
  <PopoverTrigger render={<Button variant="outline">Review status</Button>} />
  <PopoverContent align="start" className="w-72">
    <p className="text-sm text-muted-foreground">
      The final deck is due Friday.
    </p>
    <PopoverClose render={<Button size="sm">Done</Button>} />
  </PopoverContent>
</Popover>

Examples

01

Side-aligned context

Side placement keeps a short explanation attached to the exact control it clarifies.

<Popover>
  <PopoverTrigger
    render={
      <IconButton variant="ghost" aria-label="Explain review status">
        <Info />
      </IconButton>
    }
  />
  <PopoverContent side="right" align="start" className="w-72 rounded-none">
    <div className="flex flex-col gap-2">
      <h3 className="text-sm font-medium text-foreground">Review status</h3>
      <p className="text-sm text-muted-foreground text-pretty">
        Strategy, finance, and legal notes are combined before the final client review.
      </p>
    </div>
  </PopoverContent>
</Popover>
02

Dismissible decision card

PopoverClose and ButtonGroup keep a lightweight acknowledgement anchored to its trigger.

<Popover>
  <PopoverTrigger render={<Button variant="outline">Decision needed</Button>} />
  <PopoverContent align="start" className="w-80 rounded-none">
    <div className="flex flex-col gap-4">
      <div className="flex flex-col gap-2">
        <Badge variant="warning" dot>Client input</Badge>
        <h3 className="text-sm font-medium text-foreground">Confirm the working capital scenario</h3>
        <p className="text-sm text-muted-foreground text-pretty">
          The team needs a base-case assumption before publishing the next cash bridge.
        </p>
      </div>
      <ButtonGroup
        primaryAction={<PopoverClose render={<Button size="sm">Done</Button>} />}
        secondaryAction={<PopoverClose render={<Button variant="outline" size="sm">Later</Button>} />}
      />
    </div>
  </PopoverContent>
</Popover>
03

Small form helper

A compact Field and Input can live in a popover when the edit is local and non-blocking.

<Popover>
  <PopoverTrigger render={<Button variant="outline">Edit label</Button>} />
  <PopoverContent align="start" className="w-80 rounded-none">
    <div className="flex flex-col gap-4">
      <Field label="Workspace label">
        <Input defaultValue="Board update" />
      </Field>
      <PopoverClose render={<Button size="sm">Save label</Button>} />
    </div>
  </PopoverContent>
</Popover>

When to use it

Use it for

  • Anchored contextual content that is longer than a tooltip but lighter than a modal.
  • Small local edits, explanations, or acknowledgement cards tied to one trigger.
  • Dismissible content where the user can keep working in the page behind it.

Reach for something else

  • One-line hover hints — use Tooltip instead.
  • Command lists, links, or radio choices — use Menu instead.
  • Blocking decisions or multi-step forms — use Dialog instead.
  • Large side panels that need scrollable space — use Drawer instead.

Props

PropTypeDefaultDescription
Popoveropen?, defaultOpen?, onOpenChange?The Base UI state root for the popover.
PopoverTrigger / PopoverCloserender: ReactElementOpen and close slots rendered through Base UI's render prop, so the passed element keeps its full API.
PopoverContentside?, align?, sideOffset?, alignOffset?side "bottom", align "center", sideOffset 8The portalled popup surface and positioning options.

See also