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#
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).