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
});
 
return (
    <>
        <button ref={setAnchorEl}></button>
        <div ref={setContentEl}></div>
    </>
);

usePositioner computes placement coordinates for a floating element relative to an anchor, with automatic overflow handling — used internally by Popover.Positioner, Select.Positioner, and similar sub-components.

Features#

  • Dual-mode positioning: native CSS Anchor Positioning API with automatic JavaScript fallback for older browsers
  • side and align control preferred placement; flip and shift handle viewport overflow
  • state.actualSide and state.actualAlign reflect the computed placement after flip/shift
  • Arrow-aware offset calculation: sideOffset auto-derives from arrow element size when not explicitly set
  • CSS custom properties for arrow positioning (--placer-arrow-x, --placer-arrow-y, --placer-arrow-left, --placer-arrow-top)
  • updatePlacement() for imperative recalculation
  • Automatic z-index management via autoZIndex
  • RAF-throttled scroll/resize listeners with ResizeObserver for both anchor and content

Behavior#

Side and Align#

Set side to control which edge the content appears on. Set align to control cross-axis alignment.

usePositioner({ anchor, content, side: 'top', align: 'start' });

Supported values: side accepts 'top', 'right', 'bottom', 'left'. align accepts 'start', 'center', 'end'.

Flip#

Set flip to automatically move content to the opposite side when there is insufficient space. Enabled by default.

usePositioner({ anchor, content, side: 'top', flip: true });

Shift#

Set shift to slide content along the cross-axis to stay within the viewport boundary. Enabled by default.

usePositioner({ anchor, content, shift: true });

Side Offset and Align Offset#

Set sideOffset to add distance between the anchor and content. Set alignOffset to shift along the cross-axis.

usePositioner({ anchor, content, sideOffset: 12, alignOffset: 4 });

When sideOffset is not set and an arrow element is provided, the offset is automatically calculated from the arrow size.

Arrow Positioning#

Pass an arrow element to enable arrow-aware positioning. The hook sets CSS custom properties on the content element for arrow placement.

usePositioner({ anchor, content, arrow: arrowEl });

Available CSS custom properties on the content element:

var(--placer-arrow-x)      /* center X of arrow */
var(--placer-arrow-y)      /* center Y of arrow */
var(--placer-arrow-left)   /* left offset of arrow */
var(--placer-arrow-top)    /* top offset of arrow */
var(--transform-origin)    /* transform origin pointing to anchor */

Hide When Detached#

Set hideWhenDetached to hide the content when the anchor scrolls out of the boundary.

usePositioner({ anchor, content, hideWhenDetached: true });

Strategy#

Set strategy to 'absolute' for absolute positioning relative to the offset parent. Defaults to 'fixed'.

usePositioner({ anchor, content, strategy: 'absolute' });

Boundary#

Pass a boundary element to constrain placement within a specific container (JavaScript fallback mode only).

usePositioner({ anchor, content, boundary: containerEl });

Placement Change Callback#

Use onPlacementChange to react when the actual placement changes due to flip or shift.

usePositioner({
    anchor,
    content,
    onPlacementChange: ({ side, align }) => console.log(side, align)
});

Imperative Update#

Call updatePlacement() to force a recalculation when content dimensions change dynamically.

const positioner = usePositioner({ anchor, content });
 
positioner.updatePlacement();

Custom Styling with Data Attributes#

The hook sets 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).