{
  "title": "Popover",
  "description": "Non-modal floating panel anchored to a trigger — for help content, color pickers, profile previews.",
  "url": "/docs/components/popover",
  "since": "0.0.6",
  "tags": [
    "overlay"
  ],
  "platform": "both",
  "source": "---\ntitle: Popover\ndescription: Non-modal floating panel anchored to a trigger — for help content, color pickers, profile previews.\nsince: 0.0.6\ntags: [overlay]\nplatform: both\ncategory: overlays\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { Callout } from 'fumadocs-ui/components/callout';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"Popover\" />\n\n## At a glance\n\n- Compose: `Popover`, `PopoverTrigger`, `PopoverContent`. Trigger uses `asChild` by default — wrap any element (Button, Link, custom Pressable) and it becomes the activator.\n- Cross-platform: web uses `position: fixed` with a measured trigger rect so it escapes any ancestor `overflow: hidden`. Native uses RN `<Modal>` with a transparent backdrop and tap-outside-to-close.\n- Non-modal: focus is NOT trapped — the user can tab back out as normal. Dismisses on outside click and Escape.\n- Accessibility: trigger gets `aria-haspopup=\"dialog\"` + `aria-expanded`; content gets `role=\"dialog\"` (no `aria-modal`).\n\n<Callout type=\"info\" title=\"Popover vs Tooltip vs Dialog\">\n    - **Popover** — anchored, non-modal, can hold interactive content (forms, buttons, color pickers). Dismisses on outside click + Escape.\n    - **Tooltip** — anchored, non-interactive, short hover-triggered hints (one or two words to a sentence). Use Tooltip when there's nothing to click inside.\n    - **Dialog** — modal, centered, traps focus, locks scroll. Use Dialog when the user must respond before continuing.\n</Callout>\n\n## Preview\n\n<Preview name=\"popover-basic\" />\n\n## Form inside a popover\n\nThe content is non-modal, so a small form (rename, quick edit, filter) fits cleanly without dragging the user into a full dialog.\n\n<Preview name=\"popover-form\" />\n\n## Anatomy\n\n| Subcomponent | Role |\n|---|---|\n| `Popover` | Root — owns open state (controlled or uncontrolled). |\n| `PopoverTrigger` | Element that toggles the popover. `asChild` by default. |\n| `PopoverContent` | The floating surface. Renders only while open. |\n\n## Positioning\n\n`PopoverContent` accepts:\n\n- `side` — `'top' | 'right' | 'bottom' | 'left'` (default `'bottom'`). Which edge of the trigger to anchor on.\n- `align` — `'start' | 'center' | 'end'` (default `'center'`). Alignment along the chosen edge.\n\nThere is a 4-pixel gap between the trigger and the content so the two don't kiss. The minimum width is 200 px so help text never wraps awkwardly.\n\n## Open state — `open`, `defaultOpen`, `onOpenChange`\n\nPass `open` (paired with `onOpenChange`) for controlled mode. Pass\n`defaultOpen` when you want the popover open on first render (e.g.\nhint after a route transition). Mixing the two prefers controlled and\nignores `defaultOpen`.\n\n```tsx\n// Uncontrolled — Popover owns its open state.\n<Popover>\n    <Popover.Trigger><Button>Open</Button></Popover.Trigger>\n    <Popover.Content>...</Popover.Content>\n</Popover>\n\n// Controlled — you own the state, useful for closing from inside the content\n// after a save, or for syncing open with route state.\nconst [open, setOpen] = useState(false);\n\n<Popover open={open} onOpenChange={setOpen}>\n    <Popover.Trigger><Button>Open</Button></Popover.Trigger>\n    <Popover.Content>...</Popover.Content>\n</Popover>\n```\n\n## Props\n\n### Popover\n\n<PropsTable component=\"Popover\" />\n\n### PopoverTrigger\n\n<PropsTable component=\"PopoverTrigger\" />\n\n### PopoverContent\n\n<PropsTable component=\"PopoverContent\" />\n"
}
