Popover

Positions floating content next to an anchor, portaled above the page.

Demos

For usage examples, visit the component demo page: Popover

Import

import { Popover } from '@soroush.tech/design-system/Popover'

Props

PropTypeDefaultDescription
openboolean-If true, the popover is shown. Required.
childrenReactNode-The content of the popover.
anchorElHTMLElement | (() => HTMLElement | null) | null-Element (or getter) the popover is positioned against.
anchorReference'anchorEl' | 'anchorPosition' | 'none''anchorEl'Which anchor to position against.
anchorPosition{ top: number; left: number }-Client coordinates used when anchorReference is 'anchorPosition'.
anchorOrigin{ vertical, horizontal }{ vertical: 'top', horizontal: 'left' }Point on the anchor the popover attaches to. Keywords or px numbers.
transformOrigin{ vertical, horizontal }{ vertical: 'top', horizontal: 'left' }Point on the popover that meets the anchor.
onClose(event, reason) => void-Fired on Escape or a click outside the surface (reason from Modal).
elevationPaperElevation (0-24)8Shadow depth of the surface.
marginThresholdkeyof Theme['space'] | null2 (→ 16px)Minimum gap from the viewport edge, as a spacing token; null disables clamping/flipping.
containerHTMLElement | (() => HTMLElement | null) | null-Portal target passed to Modal.
actionRef<PopoverActions>-Imperative handle exposing updatePosition().
slotProps{ paper?: PaperProps }-Props for the paper slot (surface) - e.g. bg, p, style.
hasBackdropbooleanfalseRender a dimmed backdrop. Off by default (invisible click-away via Modal root).
disableScrollLockbooleanfalseDisable body scroll-lock; when true the popover re-positions on scroll.
disableAriaHiddenbooleanfalseSkip aria-hidden on background content for non-modal (aria-activedescendant) popovers.
shouldAutoFocus / shouldTrapFocus / shouldEnforceFocus / shouldRestoreFocusbooleantrueFocus-management flags forwarded to Modal. Set false for aria-activedescendant patterns.
shouldKeepMountedbooleanfalseKeep the content mounted while closed.
layerkeyof Theme['zOrder']'modal'Stacking layer.

Server rendering

The popover is drawn in a Portal, which renders nothing on the server: it is absent from server-rendered and prerendered HTML, and mounts on the client when it opens (on a click, hover, focus or key, or when the app sets it open). One that starts open, by its open prop, is still closed in the static HTML and opens on hydration. Its trigger renders on the server as usual.

Notes

  • Positioning is a pure function (utils/computePopoverPosition): it places the paper so its transformOrigin meets the anchor's anchorOrigin, flips to the opposite side when the preferred side would overflow the viewport (e.g. opens above the anchor near the bottom edge), and clamps within marginThreshold as a fallback. This makes the geometry fully unit-testable.
  • The surface is hidden (opacity 0) until first positioned, so it never flashes at the wrong spot.
  • Consumers that keep focus on the trigger (e.g. Select's aria-activedescendant listbox) should pass the four should*Focus flags as false.

Source code

If this page does not answer your question, have a look at the implementation of the component for more detail.