Usage
Headless hook for click-triggered popovers with focus trapping. Combines useLayer with useFocusTrap, auto-focus, light dismiss, Escape handling, and an optional hidden close button for accessible dialog-like popover behavior. Every painted surface emits the canonical popover target and deprecated popover-surface compatibility alias. A custom composition needing a distinct stable seam should pass and document its own surfaceTarget.
tsimport {usePopover} from '@astryxdesign/core/Popover'
Best practices
| Guidance | Practices |
|---|---|
| Do | Use for interactive content such as menus, pickers, forms, and command panels that need focus management. |
| Do | Prefer the Popover component for standard trigger-content pairs; use the hook for custom trigger patterns. |
| Do | Use popover as the broad surface target. Popover-surface remains supported compatibility output, but new theme source uses the canonical key. |
| Do | When a custom composition needs its own theme refinement, pass and document an owned surfaceTarget such as selector-popup. It refines the Popover surface rather than creating another anatomy part. |
| Don't | Use for non-interactive hover previews: use useHoverCard or useTooltip instead. |
Parameters
| Param | Type | Description |
|---|---|---|
onShow | () => void | Callback fired when the popover becomes visible. |
onHide | () => void | Callback fired when the popover is hidden. Use this to return focus to the trigger when needed. |
xstyle | StyleXStyles | StyleX styles applied to the popover content wrapper, after the default surface styles. |
hasLightDismiss | boolean (default: true) | Whether clicking outside dismisses the popover. |
hasEscapeDismiss | boolean (default: true) | Whether pressing Escape dismisses the popover. Only takes full effect together with hasLightDismiss: false, since native light dismiss also closes on Escape. |
hasAutoFocus | boolean (default: true) | Whether to focus the first genuine content control when opened. Dialogs with none fall back to the labeled surface; the generated close control is excluded from initial focus. |
hasCloseButton | boolean (default: true) | Whether to include a hidden close button that appears for keyboard users. |
closeButtonLabel | string (default: 'Close popover') | Accessible label for the hidden close button. |
dialogLabel | string | Accessible label for the popover dialog (only applies when role is "dialog"). Provide one when there is no visible title. |
role | 'dialog' | 'none' (default: 'dialog') | ARIA role on the content wrapper. Use "dialog" for genuine dialog content; use "none" for listbox/menu popups whose own content role should be exposed and whose trigger keeps DOM focus. |
isModal | boolean (default: true) | Whether a dialog-role popover is modal (aria-modal). Only applies when role is "dialog". |
hasSurface | boolean (default: true) | Whether to apply the default popover surface background, radius, and shadow. |
padding | 0 | 0.5 | 1 | 1.5 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | Inner padding of the painted surface on the spacing scale (0, 0.5, 1, 1.5, 2, 3, 4, 5, 6, 8, 10). Omit it and the hook paints no padding of its own; Popover passes 3 by default and 0 for a flush surface. |
surfaceTarget | string | Optional component-owned refinement target on the painted surface, without the astryx- prefix. Use and document one when a direct hook composition needs distinct theme reachability. Do not use popover-surface; it is a deprecated compatibility alias of the canonical popover target. |
Returns
| Field | Type | Description |
|---|---|---|
| triggerRef | (el: HTMLElement | null) => void | Ref callback to attach to the trigger element for CSS anchor positioning. |
| contentRef | RefObject<HTMLDivElement | null> | Ref for the popover content container used by focus trapping. |
| anchorId | string | CSS anchor name for advanced positioning cases. |
| show | (options?: {skipAutoFocus?: boolean}) => void | Imperatively show the popover. skipAutoFocus preserves current focus for input-triggered popovers. |
| hide | () => void | Imperatively hide the popover. |
| toggle | () => void | Toggle the popover open or closed. |
| isOpen | boolean | Whether the popover is currently open. |
| id | string | Unique ID for aria-describedby or aria-controls. |
| render | (children: ReactNode, props?: ContextRenderProps) => ReactNode | Render function for anchor-positioned popover content. Pass placement and alignment here. Logical: start/end resolve against the popover's own inherited direction (RTL mirrors in pure CSS). |
| triggerProps | {aria-haspopup: "dialog" | "true"; aria-expanded: boolean; aria-controls: string} | ARIA attributes to spread onto the trigger element. aria-haspopup reflects the popover role. |
Examples
Common configurations, variations, and states.Custom quick-actions popover using usePopover for trigger refs, ARIA attributes, and focus trapping.
Use with shadcn
Already using the shadcn registry workflow? Install the real Astryx package and a local public re-export. Component implementation source stays in Astryx. How compatibility works.
This install URL expires with the draft preview.bashnpx shadcn@4.19.0 add https://astryx-7j9u45c5d-fbopensource.vercel.app/shadcn/hooks/use-popover.json