PopoverPanel
Overlays

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.

PopoverPanelProps props
PropTypeDefaultDescription
triggerReactElement—Element that opens the panel on click.
childrenReactNode | ((api: { close: () => void }) => ReactNode)—Panel content. Render prop receives a close function.
openboolean—Controlled open state.
defaultOpenboolean—Uncontrolled initial open state.
onOpenChange(open: boolean) => void—Controlled change handler.
placementPlacement"bottom-start"Popover placement relative to trigger.
offsetnumber6Gap between trigger and panel in px.
matchWidthboolean—Panel takes at least the trigger's width.
widthnumber—Fixed panel width in px.
unstyledboolean—Skip surface styling, render children bare inside the positioner.
aria-labelstring—Accessible label for the panel dialog.

Sub-components

PopoverHeader

PopoverHeaderProps props
PropTypeDefaultDescription
childrenReactNode—Title text.
rightReactNode—Right-aligned content.

PopoverBody

PopoverBodyProps props
PropTypeDefaultDescription
childrenReactNode—Padded content region.

PopoverFooter

PopoverFooterProps props
PropTypeDefaultDescription
childrenReactNode—Right-aligned action row.

Preview

PopoverPanel

Examples

Filter popover with render prop

ImportTSX
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

Controlled PopoverPanelTSX
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

matchWidthTSX
<PopoverPanel trigger={<Button fullWidth>Actions ▾</Button>} matchWidth>
  <PopoverBody>Content that matches the trigger width…</PopoverBody>
</PopoverPanel>