Popover

Floating content anchored to a trigger element. Non-modal by default — clicks outside dismiss it. Use for inline forms, info, or quick actions.

Inline form

Info card

Right-anchored

Installation

pnpm
pnpm dlx @hex-core/cli add popover

Inline form

Compact form anchored to a button — labelled inputs and a Save action stay inside the popover surface

tsx
function GoalPopover() {
  const [target, setTarget] = useState("");
  const [deadline, setDeadline] = useState("");

  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline">Set goal</Button>
      </PopoverTrigger>
      <PopoverContent className="w-72">
        <form
          className="space-y-3"
          onSubmit={(event) => {
            event.preventDefault();
            saveGoal({ target, deadline });
          }}
        >
          <div className="grid gap-1.5">
            <Label htmlFor="target">Target</Label>
            <Input
              id="target"
              type="number"
              min={0}
              value={target}
              onChange={(e) => setTarget(e.target.value)}
            />
          </div>
          <div className="grid gap-1.5">
            <Label htmlFor="deadline">Deadline</Label>
            <Input
              id="deadline"
              type="date"
              value={deadline}
              onChange={(e) => setDeadline(e.target.value)}
            />
          </div>
          <Button type="submit" className="w-full">Save goal</Button>
        </form>
      </PopoverContent>
    </Popover>
  );
}

Decoupled trigger and anchor

Anchor the popover to a different element than the one that opens it — useful when the trigger is a small icon but the surface should align to a wider field

tsx
<Popover>
  <PopoverAnchor asChild>
    <div className="relative w-full max-w-sm">
      <Input placeholder="Search projects" className="pr-9" />
      <PopoverTrigger asChild>
        <Button
          variant="ghost"
          size="icon"
          aria-label="Search filters"
          className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7"
        >

        </Button>
      </PopoverTrigger>
    </div>
  </PopoverAnchor>
  <PopoverContent align="end" className="w-72">
    <h4 className="font-medium">Filters</h4>
    <p className="mt-1 text-sm text-muted-foreground">
      Aligned to the input, opened by the gear icon.
    </p>
  </PopoverContent>
</Popover>

API Reference

PropTypeDefaultDescription
open
booleanControlled open state
defaultOpen
booleanfalseDefault open state
onOpenChange
functionCallback on open state change: (open: boolean) => void
modal
booleanfalseWhen true, content outside the popover is inert

AI Guidance

When to use

Use for inline forms, quick settings, info panels, or color pickers. Anchored to a trigger, non-modal, dismisses on outside click.

When not to use

Don't use for critical tasks that interrupt (use Dialog). Don't use for hover-only info (use Tooltip or HoverCard). Don't use for menu actions (use DropdownMenu).

Common mistakes

  • Using Popover when the user must address the content (should be Dialog)
  • Missing asChild on PopoverTrigger when using a styled Button
  • Popover content too wide — keep it focused and compact

Accessibility

Radix manages focus, aria-expanded on the trigger, and closes on Escape. Content is portalled to body so stacking contexts don't clip it.

Token budget: 400