Floating UI DOM is the web implementation of Floating UI, providing a complete solution for positioning floating elements like tooltips, popovers, dropdowns, and menus. It wraps @floating-ui/core with DOM-specific interface logic and offers robust anchor positioning that ensures floating elements stay anchored to their reference elements while avoiding viewport collisions.
npm install @floating-ui/domimport { computePosition, autoUpdate } from "@floating-ui/dom";For middleware:
import {
offset,
flip,
shift,
autoPlacement,
size,
hide,
arrow,
inline,
limitShift,
detectOverflow
} from "@floating-ui/dom";For platform:
import { platform } from "@floating-ui/dom";For types:
import type {
Placement,
Strategy,
Middleware,
MiddlewareData,
MiddlewareState,
ComputePositionReturn,
VirtualElement,
Boundary,
RootBoundary
} from "@floating-ui/dom";CommonJS:
const { computePosition, autoUpdate, offset, flip, shift } = require("@floating-ui/dom");import { computePosition, offset, flip, shift } from "@floating-ui/dom";
// Get reference elements
const referenceElement = document.querySelector("#reference");
const floatingElement = document.querySelector("#floating");
// Compute position
const { x, y } = await computePosition(referenceElement, floatingElement, {
placement: 'bottom',
middleware: [
offset(10), // Add 10px offset
flip(), // Flip when overflowing
shift({ padding: 5 }) // Shift within padding
],
});
// Apply position to floating element
Object.assign(floatingElement.style, {
position: 'absolute',
left: `${x}px`,
top: `${y}px`,
});Floating UI DOM is built around several key components:
computePosition() function that calculates optimal placement coordinatesThe library uses a platform-agnostic core (@floating-ui/core) with DOM-specific implementations for element measurements, clipping detection, and coordinate calculations.
Computes the x and y coordinates that will place the floating element next to a given reference element.
function computePosition(
reference: ReferenceElement,
floating: FloatingElement,
options?: Partial<ComputePositionConfig>
): Promise<ComputePositionReturn>;
type ReferenceElement = Element | VirtualElement;
type FloatingElement = HTMLElement;
interface ComputePositionConfig {
placement?: Placement;
middleware?: Array<Middleware | null | undefined | false>;
strategy?: Strategy;
platform?: Platform;
}
interface ComputePositionReturn {
x: number;
y: number;
placement: Placement;
strategy: Strategy;
middlewareData: MiddlewareData;
}Automatically updates the position of the floating element when necessary, returning a cleanup function.
function autoUpdate(
reference: ReferenceElement,
floating: FloatingElement,
update: () => void,
options?: AutoUpdateOptions
): () => void;
interface AutoUpdateOptions {
ancestorScroll?: boolean;
ancestorResize?: boolean;
elementResize?: boolean;
layoutShift?: boolean;
animationFrame?: boolean;
}Collection of middleware functions that modify positioning behavior and provide positioning data.
// Core middleware functions
function offset(options?: OffsetOptions): Middleware;
function flip(options?: FlipOptions | Derivable<FlipOptions>): Middleware;
function shift(options?: ShiftOptions | Derivable<ShiftOptions>): Middleware;
function autoPlacement(options?: AutoPlacementOptions | Derivable<AutoPlacementOptions>): Middleware;
function size(options?: SizeOptions | Derivable<SizeOptions>): Middleware;
function hide(options?: HideOptions | Derivable<HideOptions>): Middleware;
function arrow(options: ArrowOptions | Derivable<ArrowOptions>): Middleware;
function inline(options?: InlineOptions | Derivable<InlineOptions>): Middleware;
// Utility
function detectOverflow(
state: MiddlewareState,
options?: DetectOverflowOptions | Derivable<DetectOverflowOptions>
): Promise<SideObject>;
function limitShift(options?: LimitShiftOptions | Derivable<LimitShiftOptions>): {
options: any;
fn: (state: MiddlewareState) => Coords;
};DOM platform implementation providing element measurements and positioning calculations.
const platform: Platform;
interface Platform {
// Required methods
getElementRects(args: {
reference: ReferenceElement;
floating: FloatingElement;
strategy: Strategy;
}): Promise<ElementRects>;
getClippingRect(args: {
element: Element;
boundary: Boundary;
rootBoundary: RootBoundary;
strategy: Strategy;
}): Promise<Rect>;
getDimensions(element: Element): Promise<Dimensions>;
// Optional methods
convertOffsetParentRelativeRectToViewportRelativeRect(args: {
elements?: Elements;
rect: Rect;
offsetParent: Element;
strategy: Strategy;
}): Promise<Rect>;
getOffsetParent(element: Element, polyfill?: (element: HTMLElement) => Element | null): Promise<Element | Window>;
isElement(value: unknown): Promise<boolean>;
getDocumentElement(element: Element): Promise<HTMLElement>;
getClientRects(element: Element): Promise<Array<ClientRectObject>>;
isRTL(element: Element): Promise<boolean>;
getScale(element: HTMLElement): Promise<{x: number; y: number}>;
}// Element types
interface VirtualElement {
getBoundingClientRect(): ClientRectObject;
getClientRects?(): Array<ClientRectObject> | DOMRectList;
contextElement?: Element;
}
interface ClientRectObject {
width: number;
height: number;
top: number;
right: number;
bottom: number;
left: number;
x: number;
y: number;
}
interface Elements {
reference: ReferenceElement;
floating: FloatingElement;
}
// Middleware types
interface Middleware {
name: string;
options?: any;
fn(state: MiddlewareState): Promise<MiddlewareReturn>;
}
interface MiddlewareState {
x: number;
y: number;
initialPlacement: Placement;
placement: Placement;
strategy: Strategy;
middlewareData: MiddlewareData;
rects: ElementRects;
elements: Elements;
}
interface MiddlewareReturn extends Partial<Coords> {
data?: {
[key: string]: any;
};
reset?: boolean | {
placement?: Placement;
rects?: boolean | ElementRects;
};
}
interface MiddlewareData {
[key: string]: any;
arrow?: Partial<Coords> & {
centerOffset: number;
alignmentOffset?: number;
};
autoPlacement?: {
index?: number;
overflows: Array<{
placement: Placement;
overflows: Array<number>;
}>;
};
flip?: {
index?: number;
overflows: Array<{
placement: Placement;
overflows: Array<number>;
}>;
};
hide?: {
referenceHidden?: boolean;
escaped?: boolean;
referenceHiddenOffsets?: SideObject;
escapedOffsets?: SideObject;
};
offset?: Coords & {
placement: Placement;
};
shift?: Coords & {
enabled: {
[key in Axis]: boolean;
};
};
size?: {
availableWidth: number;
availableHeight: number;
};
inline?: {
alignmentOffset: number;
};
}
// Utility types
type Derivable<T> = (state: MiddlewareState) => T;
type Boundary = 'clippingAncestors' | Element | Array<Element> | Rect;
type RootBoundary = 'viewport' | 'document' | Rect;
type Axis = 'x' | 'y';
// Placement and positioning types
type Placement = 'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end';
type Strategy = 'absolute' | 'fixed';
type Side = 'top' | 'right' | 'bottom' | 'left';
type Alignment = 'start' | 'end';
// Geometry types
interface Rect {
width: number;
height: number;
x: number;
y: number;
}
interface Coords {
x: number;
y: number;
}
interface Dimensions {
width: number;
height: number;
}
interface ElementRects {
reference: Rect;
floating: Rect;
}
interface SideObject {
top: number;
right: number;
bottom: number;
left: number;
}// Backwards compatibility - will be removed in next major version
function getOverflowAncestors(element: Element): Array<Element>;