usePosition#
Hook to keep a floating element anchored to a reference element, with preferred side and alignment, automatic repositioning on scroll and resize, and optional arrow offset data. It wraps Floating UI and is the positioning layer Popover and Tooltip use internally; reach for it when building your own custom floating components.
Demo#
import { usePosition } from 'hono-preact-ui';
import { useRef, useState } from 'preact/hooks';
interface UsePositionExampleProps {
side?: 'top' | 'right' | 'bottom' | 'left';
align?: 'start' | 'center' | 'end';
}
// Demonstrates usePosition: a button anchors a floating box that tracks it via
// position:fixed. The resolved side/align (after collision handling) is shown
// inside the box. Styling: .docs-useposition* in root.css.
export function UsePositionExample({
side = 'bottom',
align = 'center',
}: UsePositionExampleProps) {
const anchorRef = useRef<HTMLButtonElement>(null);
const floatingRef = useRef<HTMLDivElement>(null);
const [open, setOpen] = useState(false);
const pos = usePosition({ open, anchorRef, floatingRef, side, align });
return (
<>
<button
ref={anchorRef}
type="button"
class="docs-useposition-anchor"
onClick={() => setOpen((o) => !o)}
>
{open ? 'Hide' : 'Show'} box
</button>
{open ? (
<div
ref={floatingRef}
class="docs-useposition-box"
data-side={pos.side}
data-align={pos.align}
>
resolved: {pos.side} / {pos.align}
</div>
) : null}
</>
);
}
Signature#
import { usePosition } from 'hono-preact-ui';
function usePosition(opts: UsePositionOptions): PositionState;
interface UsePositionOptions {
open: boolean;
anchorRef: RefObject<HTMLElement>;
floatingRef: RefObject<HTMLElement>;
arrowRef?: RefObject<HTMLElement>;
getAnchorRect?: ClientRectGetter; // position against a point/virtual element
side?: 'top' | 'right' | 'bottom' | 'left'; // default 'bottom'
align?: 'start' | 'center' | 'end'; // default 'center'
offset?: number; // gap in px, default 8
}
interface PositionState {
side: 'top' | 'right' | 'bottom' | 'left';
align: 'start' | 'center' | 'end';
arrowX: number | null;
arrowY: number | null;
}
Options#
| Option | Type | Default | Notes |
|---|---|---|---|
open | boolean | none | Positioning runs only while open. |
anchorRef | RefObject | none | The reference element. |
floatingRef | RefObject | none | The element being positioned. |
arrowRef | RefObject | optional | Enables arrow positioning. |
getAnchorRect | ClientRectGetter | optional | Position against a point or virtual element instead of anchorRef (e.g. a pointer position). |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Preferred side. |
align | 'start' | 'center' | 'end' | 'center' | Alignment along the side. |
offset | number | 8 | Gap in pixels. |
Returns#
| Field | Type | Description |
|---|---|---|
side | 'top' | 'right' | 'bottom' | 'left' | Resolved side after collision handling. |
align | 'start' | 'center' | 'end' | Resolved alignment. |
arrowX | number | null | Arrow x offset in px, or null. |
arrowY | number | null | Arrow y offset in px, or null. |
Helpers#
Two pure helpers convert between the hook's (side, align) model and a Floating
UI Placement string. Both are exported from hono-preact-ui for building your
own positioning logic on top of the same mapping the components use.
import { placementFor, sideAlignFromPlacement } from 'hono-preact-ui';
// (side, align) -> Placement: 'center' is the bare side; 'start'/'end' add the suffix.
function placementFor(
side: 'top' | 'right' | 'bottom' | 'left',
align: 'start' | 'center' | 'end'
): Placement; // e.g. ('bottom', 'start') -> 'bottom-start'
// Placement -> (side, align): the inverse, for rendering data-side / data-align.
function sideAlignFromPlacement(placement: Placement): {
side: 'top' | 'right' | 'bottom' | 'left';
align: 'start' | 'center' | 'end';
};
Placement is the Floating UI placement union (e.g. 'bottom', 'bottom-start', 'left-end').