PopoverPanel
Renders arbitrary content in a positioned, dismissible panel anchored to a trigger.
Component behavior
PopoverPanel positions arbitrary content against a trigger and dismisses it on outside click. Unlike dialogs, it does not block the page, so the user can interact with the rest of the UI while the panel is open.
Use cases
- Filter editors, column pickers, or other content tied to one control.
- Click-activated content without page-wide interaction blocking.
- Custom anchored panels.
Alternatives
DropdownMenu: the panel contains a list of commands.
Select: the user selects one option.
HoverCard: a passive preview should open on hover.
Modal: the task requires modal interaction.
Props
PopoverPanel is the base for DropdownMenu and HoverCard. The trigger is cloned with ref and onClick — it must forward its ref to a DOM node.
| Prop | Type | Default | Description |
|---|---|---|---|
trigger | ReactElement | — | Element that opens the panel on click. |
children | ReactNode | ((api: { close: () => void }) => ReactNode) | — | Panel content. Render prop receives a close function. |
open | boolean | — | Controlled open state. |
defaultOpen | boolean | — | Uncontrolled initial open state. |
onOpenChange | (open: boolean) => void | — | Controlled change handler. |
placement | Placement | "bottom-start" | Popover placement relative to trigger. |
offset | number | 6 | Gap between trigger and panel in px. |
matchWidth | boolean | — | Panel takes at least the trigger's width. |
width | number | — | Fixed panel width in px. |
unstyled | boolean | — | Skip surface styling, render children bare inside the positioner. |
aria-label | string | — | Accessible label for the panel dialog. |
Sub-components
PopoverHeader
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Title text. |
right | ReactNode | — | Right-aligned content. |
PopoverBody
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Padded content region. |
PopoverFooter
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Right-aligned action row. |
Preview
Examples
Filter popover with render prop
import {
PopoverPanel,
PopoverHeader,
PopoverBody,
PopoverFooter,
Button,
Checkbox,
Stack,
} from "@solarius/onset";
import { IconFilter } from "@tabler/icons-react";
<PopoverPanel
trigger={<Button leftIcon={<IconFilter size={14} />}>Filters</Button>}
placement="bottom-start"
width={260}
>
{({ close }) => (
<>
<PopoverHeader
right={
<Button variant="ghost" size="sm" onClick={close}>
Reset
</Button>
}
>
Filters
</PopoverHeader>
<PopoverBody>
<Stack gap="2">
<Checkbox label="Active only" />
<Checkbox label="With errors" />
<Checkbox label="Paused" />
</Stack>
</PopoverBody>
<PopoverFooter>
<Button variant="primary" size="sm" onClick={close}>
Apply
</Button>
</PopoverFooter>
</>
)}
</PopoverPanel>;Controlled open state
const [open, setOpen] = useState(false);
<PopoverPanel
open={open}
onOpenChange={setOpen}
trigger={<Button>Settings</Button>}
placement="bottom-end"
>
<PopoverHeader>Settings</PopoverHeader>
<PopoverBody>Panel content…</PopoverBody>
</PopoverPanel>;Match trigger width
<PopoverPanel trigger={<Button fullWidth>Actions ▾</Button>} matchWidth>
<PopoverBody>Content that matches the trigger width…</PopoverBody>
</PopoverPanel>