usePositioner
Hook that positions a floating element relative to an anchor with automatic flip, shift, and arrow support.
Usage#
import { usePositioner } from '@primereact/headless/positioner';const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
const [contentEl, setContentEl] = React.useState<HTMLDivElement | null>(null);
const positioner = usePositioner({
anchor: anchorEl,
content: contentEl,
side: 'bottom',
sideOffset: 8,
flip: true
});
<>
<button ref={setAnchorEl}></button>
<div ref={setContentEl}></div>
</>;usePositioner computes placement for a floating element relative to an anchor, handling overflow with flip/shift and exposing the computed placement for styling. It powers Popover.Positioner, Select.Positioner, and similar primitives.
Features#
- Dual-mode positioning — uses the native CSS Anchor Positioning API when available and falls back to JavaScript otherwise
- Overflow handling —
flipmoves the content to the opposite side andshiftslides it along the cross-axis to stay within the viewport - Placement reporting —
state.actualSide/state.actualAlignreflect the final placement after flip and shift - Arrow-aware offsets — passing an arrow element auto-derives
sideOffsetfrom its size and injects CSS variables for positioning - Responsive updates — RAF-throttled scroll/resize listeners plus ResizeObserver on both anchor and content
- Imperative recalculation —
updatePlacement()forces a new layout when content dimensions change outside the observed events
Working with callbacks#
Pick preferred placement#
Use side and align to express the preferred edge and cross-axis alignment.
usePositioner({ anchor, content, side: 'top', align: 'start' });side accepts 'top', 'right', 'bottom', or 'left'. align accepts 'start', 'center', or 'end'.
Attach an arrow#
Pass an arrow element and the hook exposes CSS variables for positioning the arrow tip, derived from the final placement.
usePositioner({ anchor, content, arrow: arrowEl });var(--px-placer-arrow-x) /* center X of arrow */
var(--px-placer-arrow-y) /* center Y of arrow */
var(--px-placer-arrow-left) /* left offset of arrow */
var(--px-placer-arrow-top) /* top offset of arrow */
var(--px-transform-origin) /* transform origin pointing to anchor */React to placement changes#
Register onPlacementChange to run logic when flip or shift moves the content — for example, swapping an arrow direction or updating an aria-live message.
usePositioner({
anchor,
content,
onPlacementChange: ({ side, align }) => setPlacement({ side, align })
});Constrain placement inside a container#
Pass a boundary element (JavaScript fallback mode) to keep the floating element inside a specific scroll region instead of the viewport.
usePositioner({ anchor, content, boundary: containerEl });Hide when the anchor leaves the viewport#
Enable hideWhenDetached so the content hides automatically once the anchor scrolls out of the boundary.
usePositioner({ anchor, content, hideWhenDetached: true });Force a recompute#
Call updatePlacement() after imperative content changes (such as loading data) so the hook picks up the new dimensions.
const positioner = usePositioner({ anchor, content });
positioner.updatePlacement();Styling with data attributes#
The hook writes data-side and data-align on the content and arrow element for placement-aware CSS.
[data-side='bottom'] {
margin-top: 4px;
}
[data-side='top'] {
margin-bottom: 4px;
}
[data-side='bottom'][data-align='start'] {
/* bottom-start placement */
}API#
usePositioner#
| Name | Type | Default |
|---|---|---|
side | SideType | — |
| The side of the positioner. | ||
align | AlignType | — |
| The align of the positioner. | ||
sideOffset | number | — |
| The side offset of the positioner. | ||
alignOffset | number | — |
| The align offset of the positioner. | ||
flip | boolean | — |
| Whether to flip the positioner. | ||
shift | boolean | — |
| Whether to shift the positioner. | ||
hideWhenDetached | boolean | — |
| Whether to hide the positioner when detached. | ||
strategy | "fixed" | "absolute" | 'fixed' |
| The positioning strategy. | ||
boundary | HTMLElement | — |
| The boundary element that constrains the positioner placement. Used in JS fallback mode only; CSS Anchor mode uses the containing block. | ||
anchor | HTMLElement | — |
| The anchor element. | ||
content | HTMLDivElement | — |
| The content element. | ||
arrow | HTMLDivElement | — |
| The arrow element. | ||
autoZIndex | boolean | true |
| Whether to automatically manage layering. | ||
baseZIndex | number | 0 |
| Base zIndex value to use in layering. | ||
onPlacementChange | (placement: { side: SideType; align: AlignType }) => void | — |
| Callback invoked when the actual placement changes due to flip or shift. | ||
Accessibility#
usePositioner is a structural positioning utility. It does not introduce ARIA roles or keyboard interactions. Accessibility concerns are handled by the consumer component (e.g., Popover, Tooltip, Select).