Skip to content

API Overview

SymbolPurposeExecution modeCommon gotcha
createPositioner()Lifecycle-owned floating positioningSyncStarts immediately; create after mount
computePosition()Low-level geometry computationSyncCaller owns CSS application
autoUpdate()Listen for geometry changesSyncCall returned cleanup
createReactivePositioner()Optional Ripple position readableSyncRequires @vielzeug/ripple
Middleware factoriesAdjust placement and sizeSyncOrder is explicit

Package Entry Points

ImportPurpose
@vielzeug/orbitPositioner, computation, updates, middleware, and types.
@vielzeug/orbit/reactiveOptional Ripple position adapter.
@vielzeug/orbit/presetsPreset placement and middleware options.
@vielzeug/orbit/devtoolsDevelopment overlay.

Core Functions

createPositioner()

ts
function createPositioner(
  reference: ReferenceElement,
  floating: HTMLElement,
  options?: PositionerOptions,
): Positioner;

Creates an active positioner and applies its initial position before returning.

ParameterTypeDescription
referenceReferenceElementDOM or virtual anchor.
floatingHTMLElementPositioned element.
optionsPositionerOptionsStrategy, clipping, middleware, updates, and application callback.

Returns: Positioner.

ts
import { createPositioner } from '@vielzeug/orbit';

const positioner = createPositioner(trigger, tooltip);
positioner.dispose();
MemberReturnContract
update()voidRecomputes and applies position.
getPosition()ComputePositionResultLatest applied result.
dispose()voidStops updates and aborts disposal signal.

computePosition()

ts
function computePosition(
  reference: ReferenceElement,
  floating: HTMLElement,
  options?: ComputePositionOptions,
): ComputePositionResult;

Calculates position without applying DOM styles or creating listeners.

Returns: ComputePositionResult.

autoUpdate()

ts
function autoUpdate(
  reference: ReferenceElement,
  floating: HTMLElement,
  update: () => void,
  options?: AutoUpdateOptions,
): () => void;

Calls update immediately, then on relevant scroll, viewport, resize, and optional animation-frame changes.

Returns: cleanup callback.

Middleware

ts
type Middleware = (state: MiddlewareState) => MiddlewareResult | undefined;

Built-in factories: arrow, autoPlacement, flip, hide, inline, offset, shift, limitShift, and size.

ts
const middleware = [offset(8), flip(), shift({ padding: 6 }), size()];

middlewareData is MiddlewareData; narrow custom data at the consuming boundary.

Reactive Adapter

ts
function createReactivePositioner(
  reference: ReferenceElement,
  floating: HTMLElement,
  options?: Omit<PositionerOptions, 'apply'>,
): ReactivePositioner;

ReactivePositioner.position is Readable<ComputePositionResult>. Imported from @vielzeug/orbit/reactive.

Types

ts
type Side = 'top' | 'bottom' | 'left' | 'right';
type Alignment = 'start' | 'end';
type Placement = Side | `${Side}-${Alignment}`;

interface Rect {
  height: number;
  width: number;
  x: number;
  y: number;
}

interface VirtualReference {
  getBoundingClientRect: () => DOMRect | Rect;
  getClientRects?: () => DOMRectList | DOMRect[];
}

type ReferenceElement = Element | VirtualReference;

interface SideObject {
  bottom: number;
  left: number;
  right: number;
  top: number;
}

type Padding = number | Partial<SideObject>;

interface ArrowData {
  centerOffset: number;
  constrained: boolean;
  x?: number;
  y?: number;
}

interface FlipData {
  skippedPlacements: Placement[];
}

interface ShiftData {
  x: number;
  y: number;
}

interface HideData {
  escaped?: boolean;
  escapedOffsets?: SideObject;
  referenceHidden?: boolean;
  referenceHiddenOffsets?: SideObject;
}

interface SizeData {
  availableHeight: number;
  availableWidth: number;
}

interface MiddlewareData {
  arrow?: ArrowData;
  flip?: FlipData;
  hide?: HideData;
  shift?: ShiftData;
  size?: SizeData;
  [key: string]: unknown;
}

interface MiddlewareState {
  boundary?: Element | Rect;
  elements: { floating: HTMLElement; reference: ReferenceElement };
  initialPlacement: Placement;
  middlewareData: MiddlewareData;
  padding?: Padding;
  placement: Placement;
  rects: { floating: Rect; reference: Rect };
  x: number;
  y: number;
}

type MiddlewareReset = {
  placement?: Placement;
  rects?: MiddlewareState['rects'];
  remeasure?: boolean;
};

interface MiddlewareResult {
  data?: MiddlewareData;
  placement?: Placement;
  reset?: MiddlewareReset;
  x?: number;
  y?: number;
}

type Middleware = (state: MiddlewareState) => MiddlewareResult | undefined;

interface ComputePositionResult {
  middlewareData: MiddlewareData;
  placement: Placement;
  x: number;
  y: number;
}

interface ComputePositionOptions {
  boundary?: Element | Rect;
  containingBlock?: Element | null;
  middleware?: readonly Middleware[];
  padding?: Padding;
  placement?: Placement;
}

interface DetectOverflowOptions {
  boundary?: Element | Rect;
  padding?: Padding;
}

type PositionStrategy = 'absolute' | 'fixed';

interface PositionerOptions extends Omit<ComputePositionOptions, 'boundary' | 'containingBlock'> {
  apply?: (result: ComputePositionResult) => void;
  autoUpdate?: AutoUpdateOptions | false;
  boundary?: ComputePositionOptions['boundary'] | 'clippingAncestors';
  strategy?: PositionStrategy;
}

interface Positioner {
  readonly disposalSignal: AbortSignal;
  dispose(): void;
  readonly disposed: boolean;
  getPosition(): ComputePositionResult;
  update(): void;
  [Symbol.dispose](): void;
}

interface AutoUpdateOptions {
  animationFrame?: boolean;
  observeAncestors?: boolean;
  observeFloating?: boolean;
  observeVisualViewport?: boolean;
  pauseWhenHidden?: boolean;
  throttle?: number;
}

interface ReactivePositioner extends Positioner {
  readonly position: Readable<ComputePositionResult>;
}

interface ArrowOptions {
  element: HTMLElement;
  padding?: Padding;
}

interface AutoPlacementOptions extends DetectOverflowOptions {
  alignment?: Alignment | null;
  allowedPlacements?: Placement[];
}

interface FlipOptions extends DetectOverflowOptions {
  fallbackPlacements?: Placement[];
}

interface HideOptions extends DetectOverflowOptions {
  strategy?: 'referenceHidden' | 'escaped' | 'both';
}

type OffsetConfig = {
  crossAxis?: number;
  mainAxis?: number;
};

type OffsetValue = number | OffsetConfig | ((state: MiddlewareState) => number | OffsetConfig);

type ShiftLimiter = (
  state: MiddlewareState,
  correction: { crossAxis: number; mainAxis: number },
) => { crossAxis: number; mainAxis: number };

interface LimitShiftOptions {
  offset?: number | ((state: MiddlewareState) => number);
}

interface ShiftOptions extends DetectOverflowOptions {
  crossAxis?: boolean;
  limiter?: ShiftLimiter;
}

interface InlineOptions {
  padding?: Padding;
  x?: number;
  y?: number;
}

type SizeOptions = DetectOverflowOptions;

interface PositioningPreset {
  middleware: Middleware[];
  placement: Placement;
}

interface PresetOptions {
  offset?: number;
  padding?: number;
  placement?: Placement;
}

Errors

ErrorTriggerNotable properties
OrbitConfigErrorInvalid middleware reset configurationExtends OrbitError
OrbitErrorBase Orbit errorinstanceof OrbitError narrows Orbit errors