Introducing PrimeReact v11-alpha 🎉Discover Now

usePositioner

Hook that positions a floating element relative to an anchor with automatic flip, shift, and arrow support.

basic-demo

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 — flip moves the content to the opposite side and shift slides it along the cross-axis to stay within the viewport
  • Placement reporting — state.actualSide / state.actualAlign reflect the final placement after flip and shift
  • Arrow-aware offsets — passing an arrow element auto-derives sideOffset from 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#

NameTypeDefault
sideSideType—
The side of the positioner.
alignAlignType—
The align of the positioner.
sideOffsetnumber—
The side offset of the positioner.
alignOffsetnumber—
The align offset of the positioner.
flipboolean—
Whether to flip the positioner.
shiftboolean—
Whether to shift the positioner.
hideWhenDetachedboolean—
Whether to hide the positioner when detached.
strategy"fixed" | "absolute"'fixed'
The positioning strategy.
boundaryHTMLElement—
The boundary element that constrains the positioner placement. Used in JS fallback mode only; CSS Anchor mode uses the containing block.
anchorHTMLElement—
The anchor element.
contentHTMLDivElement—
The content element.
arrowHTMLDivElement—
The arrow element.
autoZIndexbooleantrue
Whether to automatically manage layering.
baseZIndexnumber0
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).