Re-exported positioning middleware from @floating-ui/react-dom for collision detection, placement optimization, visual enhancements, and advanced positioning behaviors.
Essential middleware for collision detection and placement optimization.
/**
* Displaces floating element from reference element by specified offset
* @param value - Offset value or configuration object
* @returns Middleware for offset positioning
*/
function offset(value?: OffsetOptions): Middleware;
type OffsetOptions =
| number
| { mainAxis?: number; crossAxis?: number; alignmentAxis?: number | null }
| ((state: MiddlewareState) => number | { mainAxis?: number; crossAxis?: number; alignmentAxis?: number | null });
/**
* Flips floating element to opposite placement when insufficient space
* @param options - Flip behavior configuration
* @returns Middleware for flip positioning
*/
function flip(options?: FlipOptions): Middleware;
interface FlipOptions {
mainAxis?: boolean;
crossAxis?: boolean;
fallbackPlacements?: Array<Placement>;
fallbackStrategy?: 'bestFit' | 'initialPlacement';
fallbackAxisSideDirection?: 'none' | 'start' | 'end';
flipAlignment?: boolean;
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}
/**
* Shifts floating element along specified axis to stay in view
* @param options - Shift behavior configuration
* @returns Middleware for shift positioning
*/
function shift(options?: ShiftOptions): Middleware;
interface ShiftOptions {
mainAxis?: boolean;
crossAxis?: boolean;
limitShift?: LimitShiftOptions | ((state: MiddlewareState) => LimitShiftOptions);
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}
/**
* Limits shift amount to prevent losing reference connection
* @param options - Limit shift configuration
* @returns Shift limit configuration
*/
function limitShift(options?: LimitShiftOptions): LimitShiftOptions;
interface LimitShiftOptions {
offset?: number | ((state: MiddlewareState) => number);
mainAxis?: boolean;
crossAxis?: boolean;
}Usage Examples:
import { useFloating, offset, flip, shift, limitShift } from '@floating-ui/react';
// Basic positioning with collision detection
function BasicPositioning() {
const { refs, floatingStyles } = useFloating({
placement: 'bottom',
middleware: [
offset(10), // 10px offset from reference
flip(), // Flip when no space
shift({ padding: 8 }), // Shift to stay in viewport
],
});
return (
<>
<button ref={refs.setReference}>Reference</button>
<div ref={refs.setFloating} style={floatingStyles}>
Floating element
</div>
</>
);
}
// Advanced positioning with custom options
function AdvancedPositioning() {
const { refs, floatingStyles } = useFloating({
placement: 'top-start',
middleware: [
offset({ mainAxis: 15, crossAxis: 5 }),
flip({
fallbackPlacements: ['top-end', 'bottom-start', 'bottom-end'],
fallbackStrategy: 'bestFit',
}),
shift({
padding: 16,
limitShift: limitShift({
offset: 10,
mainAxis: true,
crossAxis: false,
}),
}),
],
});
return (
<>
<button ref={refs.setReference}>Advanced</button>
<div ref={refs.setFloating} style={floatingStyles}>
Advanced positioning
</div>
</>
);
}Middleware for visual improvements and arrow positioning.
/**
* Positions arrow element pointing to reference
* @param options - Arrow configuration
* @returns Middleware for arrow positioning
*/
function arrow(options: ArrowOptions): Middleware;
interface ArrowOptions {
element: Element | null | React.MutableRefObject<Element | null>;
padding?: Padding;
}
/**
* Hides floating element when reference is not visible
* @param options - Hide behavior configuration
* @returns Middleware for visibility-based hiding
*/
function hide(options?: HideOptions): Middleware;
interface HideOptions {
strategy?: 'referenceHidden' | 'escaped';
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}
/**
* Improves positioning for inline reference elements
* @param options - Inline positioning configuration
* @returns Middleware for inline element positioning
*/
function inline(options?: InlineOptions): Middleware;
interface InlineOptions {
x?: number;
y?: number;
padding?: Padding;
}Usage Examples:
import { useFloating, arrow, hide, inline } from '@floating-ui/react';
import { useRef } from 'react';
// Arrow positioning
function ArrowTooltip() {
const arrowRef = useRef<SVGSVGElement>(null);
const { refs, floatingStyles, middlewareData } = useFloating({
middleware: [
offset(10),
arrow({ element: arrowRef, padding: 8 }),
],
});
const { x: arrowX, y: arrowY } = middlewareData.arrow || {};
return (
<>
<button ref={refs.setReference}>Hover</button>
<div ref={refs.setFloating} style={floatingStyles}>
Tooltip with arrow
<svg
ref={arrowRef}
style={{
position: 'absolute',
left: arrowX != null ? `${arrowX}px` : '',
top: arrowY != null ? `${arrowY}px` : '',
}}
width="10"
height="5"
>
<polygon points="0,0 10,0 5,5" fill="black" />
</svg>
</div>
</>
);
}
// Hide when reference is not visible
function ConditionalTooltip() {
const { refs, floatingStyles, middlewareData } = useFloating({
middleware: [
offset(10),
hide({ strategy: 'referenceHidden' }),
],
});
const { referenceHidden } = middlewareData.hide || {};
return (
<>
<button ref={refs.setReference}>Reference</button>
<div
ref={refs.setFloating}
style={{
...floatingStyles,
visibility: referenceHidden ? 'hidden' : 'visible',
}}
>
Conditionally visible
</div>
</>
);
}
// Inline text positioning
function InlineTooltip() {
const { refs, floatingStyles } = useFloating({
middleware: [
inline(),
offset(5),
flip(),
shift(),
],
});
return (
<p>
This is some text with an{' '}
<span ref={refs.setReference} style={{ background: 'yellow' }}>
inline reference
</span>
{' '}that has a tooltip.
<div ref={refs.setFloating} style={floatingStyles}>
Inline tooltip
</div>
</p>
);
}Automatically chooses optimal placement based on available space.
/**
* Automatically chooses placement to maximize available space
* @param options - Auto-placement configuration
* @returns Middleware for automatic placement selection
*/
function autoPlacement(options?: AutoPlacementOptions): Middleware;
interface AutoPlacementOptions {
crossAxis?: boolean;
alignment?: Alignment | null;
allowedPlacements?: Array<Placement>;
autoAlignment?: boolean;
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}Usage Example:
import { useFloating, autoPlacement, offset } from '@floating-ui/react';
// Automatic placement selection
function AutoPlacementTooltip() {
const { refs, floatingStyles } = useFloating({
middleware: [
offset(10),
autoPlacement({
allowedPlacements: ['top', 'bottom', 'left', 'right'],
autoAlignment: true,
}),
],
});
return (
<>
<button ref={refs.setReference}>Auto Placement</button>
<div ref={refs.setFloating} style={floatingStyles}>
Automatically positioned
</div>
</>
);
}
// Constrained auto-placement
function ConstrainedAutoPlacement() {
const { refs, floatingStyles } = useFloating({
middleware: [
autoPlacement({
allowedPlacements: ['top-start', 'top-end', 'bottom-start', 'bottom-end'],
crossAxis: false,
alignment: 'start',
}),
],
});
return (
<>
<button ref={refs.setReference}>Constrained</button>
<div ref={refs.setFloating} style={floatingStyles}>
Constrained auto-placement
</div>
</>
);
}Resizes floating element based on available space.
/**
* Resizes floating element based on available space
* @param options - Size configuration
* @returns Middleware for size-based adjustments
*/
function size(options?: SizeOptions): Middleware;
interface SizeOptions {
apply(args: {
state: MiddlewareState;
availableWidth: number;
availableHeight: number;
elements: Elements;
}): void;
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}Usage Example:
import { useFloating, size, flip, offset } from '@floating-ui/react';
// Dynamic sizing based on available space
function ResponsiveDropdown() {
const { refs, floatingStyles } = useFloating({
middleware: [
offset(5),
flip(),
size({
apply({ availableWidth, availableHeight, elements }) {
if (elements.floating) {
Object.assign(elements.floating.style, {
maxWidth: `${availableWidth}px`,
maxHeight: `${Math.max(100, availableHeight - 20)}px`,
});
}
},
}),
],
});
return (
<>
<button ref={refs.setReference}>Responsive</button>
<div
ref={refs.setFloating}
style={{
...floatingStyles,
background: 'white',
border: '1px solid gray',
padding: '8px',
overflowY: 'auto',
}}
>
<div style={{ height: '300px' }}>
<p>This content will be sized based on available space</p>
<p>It will never exceed the viewport boundaries</p>
<p>And will show scrollbars if needed</p>
</div>
</div>
</>
);
}Core positioning calculation function for manual positioning.
/**
* Core positioning calculation function
* @param reference - Reference element or virtual element
* @param floating - Floating element
* @param options - Positioning configuration
* @returns Promise with positioning data
*/
function computePosition(
reference: ReferenceElement,
floating: FloatingElement,
options?: ComputePositionConfig
): Promise<ComputePositionReturn>;
interface ComputePositionConfig {
placement?: Placement;
strategy?: Strategy;
middleware?: Array<Middleware | null | undefined | false>;
platform?: Platform;
}
interface ComputePositionReturn {
x: number;
y: number;
placement: Placement;
strategy: Strategy;
middlewareData: MiddlewareData;
}Automatically updates position when needed based on DOM changes and scroll events.
/**
* Automatically updates position when needed
* @param reference - Reference element
* @param floating - Floating element
* @param update - Update function to call
* @param options - Auto-update configuration
* @returns 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;
}Additional utility functions for overflow detection and platform methods.
/**
* Detects when floating element overflows boundaries
* @param state - Current middleware state
* @param options - Overflow detection options
* @returns Overflow data for each side
*/
function detectOverflow(
state: MiddlewareState,
options?: DetectOverflowOptions
): SideObject;
interface DetectOverflowOptions {
boundary?: Boundary;
rootBoundary?: RootBoundary;
elementContext?: ElementContext;
altBoundary?: boolean;
padding?: Padding;
}
/**
* Gets ancestors that can cause overflow
* @param node - Starting node
* @param boundary - Boundary element
* @returns Array of overflow ancestor elements
*/
function getOverflowAncestors(
node: Node,
boundary?: Element | 'clippingAncestors'
): Array<Element>;
/**
* Platform-specific methods for measurements and utilities
*/
declare const platform: Platform;
interface Platform {
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>;
convertOffsetParentRelativeRectToViewportRelativeRect?: (args: {
elements: Elements;
rect: Rect;
offsetParent: Element;
strategy: Strategy;
}) => Promise<Rect>;
getOffsetParent?: (element: Element) => Promise<Element | null>;
isElement?: (value: unknown) => boolean;
getClientRects?: (element: Element) => Promise<Array<ClientRect>>;
}const middleware = [
offset(10),
flip(),
shift({ padding: 5 }),
arrow({ element: arrowRef }),
];const middleware = [
offset(5),
flip({ fallbackPlacements: ['bottom', 'top'] }),
shift({ padding: 8 }),
size({
apply({ availableHeight, elements }) {
Object.assign(elements.floating.style, {
maxHeight: `${Math.max(200, availableHeight - 50)}px`,
});
},
}),
];const middleware = [
flip({ fallbackStrategy: 'bestFit' }),
shift({ padding: 10 }),
hide({ strategy: 'referenceHidden' }),
];const middleware = [
inline(),
offset(8),
flip(),
shift({ padding: 5 }),
hide(),
];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 Boundary = 'clippingAncestors' | Element | Array<Element>;
type RootBoundary = 'viewport' | 'document' | Rect;
type ElementContext = 'reference' | 'floating';
type Padding = number | Partial<SideObject>;
type Alignment = 'start' | 'end';
interface SideObject {
top: number;
right: number;
bottom: number;
left: number;
}
interface Rect {
x: number;
y: number;
width: number;
height: number;
}
interface Dimensions {
width: number;
height: number;
}
interface ElementRects {
reference: Rect;
floating: Rect;
}
interface MiddlewareState {
x: number;
y: number;
initialPlacement: Placement;
placement: Placement;
strategy: Strategy;
middlewareData: MiddlewareData;
rects: ElementRects;
platform: Platform;
elements: Elements;
}
interface Elements {
reference: ReferenceElement;
floating: FloatingElement;
}
type ReferenceElement = Element | VirtualElement;
type FloatingElement = HTMLElement;
interface VirtualElement {
getBoundingClientRect(): ClientRect | DOMRect;
contextElement?: Element;
}
interface Middleware {
name: string;
options?: any;
fn: (state: MiddlewareState) => Promise<MiddlewareReturn>;
}
interface MiddlewareReturn {
x?: number;
y?: number;
data?: Record<string, any>;
reset?: boolean | ResetValue;
}
interface MiddlewareData {
[key: string]: any;
arrow?: { x?: number; y?: number; centerOffset: number };
autoPlacement?: { index: number; overflows: Array<number> };
flip?: { index: number; overflows: Array<number> };
hide?: { referenceHidden?: boolean; escaped?: boolean };
offset?: { x: number; y: number };
shift?: { x: number; y: number };
size?: { availableWidth: number; availableHeight: number };
}