Components

Portalled popover surface for small contextual content anchored to a trigger. The popup is role="dialog"; a PopoverTitle inside names it (aria-labelledby), and PopoverDescription sets aria-describedby.

Install

npx shadcn@latest add @alix/popover

Preview

Usage

<Popover>
  <PopoverTrigger render={<Button variant="outline">Review status</Button>} />
  <PopoverContent align="start" className="w-72">
    <PopoverTitle>Client review</PopoverTitle>
    <PopoverDescription>The final deck is due Friday.</PopoverDescription>
    <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.
PopoverTitle / PopoverDescriptionchildrenName the dialog. Title's id becomes the popup's aria-labelledby, Description's its aria-describedby. Include a Title so the dialog isn't unnamed.
PopoverContentside?, align?, sideOffset?, alignOffset?side "bottom", align "center", sideOffset 8The portalled popup surface and positioning options.

See also