Popover
A non-modal floating panel anchored to its trigger. Escape or an outside press closes it.
Import
import { Popover } from "@merid/react";<Popover.Root>
<Popover.Trigger asChild>
<Button variant="secondary">Share</Button>
</Popover.Trigger>
<Popover.Content aria-label="Share">
<p>Anyone with the link can view this page.</p>
<Popover.Close asChild>
<Button variant="primary" size="sm">Copy link</Button>
</Popover.Close>
</Popover.Content>
</Popover.Root>Anatomy
<Popover.Root>
<Popover.Trigger />
<Popover.Content>
<Popover.Close />
</Popover.Content>
</Popover.Root>- Root holds the open state.
- Trigger is the
<button>the panel is anchored to. PassasChildto anchor to your own element, such as aButton. - Content is the floating panel, portalled and positioned with collision handling.
- Close is an unstyled button that closes the popover and returns focus to the trigger. Pass
asChildto render your ownButton.
Examples
Placement
placement accepts any Floating UI placement (top, bottom-start, left-end…). It flips and shifts to stay on screen.
<Popover.Content placement="top">Placed top</Popover.Content>
<Popover.Content placement="right">Placed right</Popover.Content>
<Popover.Content placement="bottom">Placed bottom</Popover.Content>
<Popover.Content placement="left">Placed left</Popover.Content>Controlled
const [open, setOpen] = useState(false);
<Popover.Root open={open} onOpenChange={setOpen}>
<Popover.Trigger asChild>
<Button variant="secondary">Details</Button>
</Popover.Trigger>
<Popover.Content aria-label="Details">Open: {String(open)}</Popover.Content>
</Popover.Root>
<span>{open ? "Open" : "Closed"}</span>API reference
Popover.Root
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | – | Controlled open state. |
defaultOpen | boolean | false | Initial open state when uncontrolled. |
onOpenChange | (open: boolean) => void | – | Called when the open state should change. |
children | ReactNode | – | Trigger and Content. |
Popover.Trigger
Accepts every <button> attribute. type defaults to "button" (not set when asChild is used).
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the single child element (e.g. your own `Button`) instead of a `<button>`, merging props, ref and handlers. |
ref | Ref<HTMLButtonElement> | – | Forwarded ref to the button. |
Popover.Content
Accepts every <div> attribute.
| Prop | Type | Default | Description |
|---|---|---|---|
placement | Placement | "bottom-start" | Preferred placement relative to the trigger. |
sideOffset | number | 8 | Distance from the trigger in px. |
aria-label | string | – | Accessible name when the content has no visible heading. |
container | Element | null | document.body | Portal target. `undefined` uses `document.body`; `null` renders nothing until the target exists. |
ref | Ref<HTMLDivElement> | – | Forwarded ref to the content element. |
Popover.Close
Accepts every <button> attribute.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the single child element (e.g. your own `Button`) instead of a `<button>`, merging props, ref and handlers. |
ref | Ref<HTMLButtonElement> | – | Forwarded ref to the button. |
Styling
.mrd-popover is the panel: --mrd-surface, 1px --mrd-line border, --mrd-radius-xl, --mrd-shadow-lg, max width min(360px, 100vw - 16px). It carries data-state="open"; the trigger carries data-state="open" | "closed". Trigger and Close are unstyled buttons; pass asChild with a Button to style them.
Accessibility
Content has role="dialog" (non-modal); the trigger has aria-haspopup="dialog", aria-expanded and aria-controls. Focus moves into the panel on open but is not trapped. See the WAI-ARIA Dialog pattern for the modal counterpart. Give Content an aria-label or a visible heading with aria-labelledby.
| Key | Action |
|---|---|
| SpaceEnter | On the trigger, toggles the popover. |
| Tab | Moves through the popover's content, then out of it. |
| Esc | Closes the popover and returns focus to the trigger. |
Guidelines
Do
Avoid
Related
- Tooltip for short, non-interactive labels.
- DropdownMenu for lists of actions.
- Dialog