Essential middleware for common positioning scenarios including offset adjustments, flip behavior, and shift behavior to keep floating elements in view.
Modifies the placement by translating the floating element along specified axes. Useful for adding gaps between reference and floating elements.
/**
* Modifies the placement by translating the floating element along the
* specified axes. A number (shorthand for mainAxis or distance), or an axes
* configuration object may be passed.
*
* @param options - Offset configuration
* @returns Middleware object
*/
function offset(options?: OffsetOptions): Middleware;
type OffsetOptions = number | {
/** Distance along the side of the floating element (gutter/margin) */
mainAxis?: number;
/** Distance along the alignment of the floating element (skidding) */
crossAxis?: number;
/** Same as crossAxis but for aligned placements only, inverts end alignment */
alignmentAxis?: number | null;
} | Derivable<number | {
mainAxis?: number;
crossAxis?: number;
alignmentAxis?: number | null;
}>;Usage Examples:
import { computePosition, offset } from "@floating-ui/core";
// Simple gap of 10px
await computePosition(reference, floating, {
middleware: [offset(10)],
platform: domPlatform
});
// Different axes
await computePosition(reference, floating, {
middleware: [
offset({
mainAxis: 10, // 10px gap from reference
crossAxis: 5, // 5px skidding along alignment
alignmentAxis: -2 // 2px toward opposite edge for aligned placements
})
],
platform: domPlatform
});
// Dynamic offset based on state
await computePosition(reference, floating, {
middleware: [
offset(({ rects }) => ({
mainAxis: rects.floating.height / 2
}))
],
platform: domPlatform
});Optimizes visibility by flipping the placement to keep it in view when the preferred placement would overflow the clipping boundary. Alternative to autoPlacement.
/**
* Optimizes the visibility of the floating element by flipping the placement
* in order to keep it in view when the preferred placement(s) will overflow
* the clipping boundary.
*
* @param options - Flip configuration
* @returns Middleware object
*/
function flip(options?: FlipOptions): Middleware;
interface FlipOptions extends DetectOverflowOptions {
/** Whether to check main axis overflow for flipping */
mainAxis?: boolean;
/** Whether to check cross axis overflow for flipping */
crossAxis?: boolean | "alignment";
/** Placements to try if preferred placement doesn't fit */
fallbackPlacements?: Array<Placement>;
/** Strategy when no placements fit */
fallbackStrategy?: "bestFit" | "initialPlacement";
/** Whether to allow fallback to perpendicular axis */
fallbackAxisSideDirection?: "none" | "start" | "end";
/** Whether to flip to opposite alignments if they fit better */
flipAlignment?: boolean;
}Usage Examples:
import { computePosition, flip } from "@floating-ui/core";
// Basic flipping
await computePosition(reference, floating, {
placement: "top",
middleware: [flip()], // Will try "bottom" if "top" overflows
platform: domPlatform
});
// Custom fallback sequence
await computePosition(reference, floating, {
placement: "top",
middleware: [
flip({
fallbackPlacements: ["bottom", "left", "right"]
})
],
platform: domPlatform
});
// Only check main axis, allow perpendicular fallback
await computePosition(reference, floating, {
placement: "top",
middleware: [
flip({
mainAxis: true,
crossAxis: false,
fallbackAxisSideDirection: "start"
})
],
platform: domPlatform
});Optimizes visibility by shifting the floating element to keep it in view when it would overflow the clipping boundary. Works along the alignment axis.
/**
* Optimizes the visibility of the floating element by shifting it in order to
* keep it in view when it will overflow the clipping boundary.
*
* @param options - Shift configuration
* @returns Middleware object
*/
function shift(options?: ShiftOptions): Middleware;
interface ShiftOptions extends DetectOverflowOptions {
/** Whether to shift along the main axis */
mainAxis?: boolean;
/** Whether to shift along the cross axis */
crossAxis?: boolean;
/** Function that limits the shifting to prevent detachment */
limiter?: {
fn: (state: MiddlewareState) => Coords;
options?: any;
};
}Usage Examples:
import { computePosition, shift, limitShift } from "@floating-ui/core";
// Basic shifting
await computePosition(reference, floating, {
placement: "top",
middleware: [shift()], // Shifts to stay within viewport
platform: domPlatform
});
// Shift with padding
await computePosition(reference, floating, {
placement: "top-start",
middleware: [
shift({
padding: 8 // Keep 8px away from viewport edges
})
],
platform: domPlatform
});
// Shift both axes with limiter
await computePosition(reference, floating, {
placement: "bottom",
middleware: [
shift({
mainAxis: true,
crossAxis: true,
limiter: limitShift() // Prevents shifting too far from reference
})
],
platform: domPlatform
});Built-in limiter for the shift() middleware that prevents excessive shifting that could detach the floating element from its reference.
/**
* Built-in limiter that will stop shift() at a certain point to prevent
* the floating element from detaching from the reference element.
*
* @param options - Limit shift configuration
* @returns Limiter object for use with shift middleware
*/
function limitShift(options?: LimitShiftOptions): {
options: any;
fn: (state: MiddlewareState) => Coords;
};
interface LimitShiftOptions {
/** Offset when limiting starts */
offset?: number | {
mainAxis?: number;
crossAxis?: number;
} | ((state: MiddlewareState) => number | {
mainAxis?: number;
crossAxis?: number;
});
/** Whether to limit main axis shifting */
mainAxis?: boolean;
/** Whether to limit cross axis shifting */
crossAxis?: boolean;
}Usage Examples:
import { computePosition, shift, limitShift } from "@floating-ui/core";
// Basic limit shift
await computePosition(reference, floating, {
middleware: [
shift({
limiter: limitShift()
})
],
platform: domPlatform
});
// Custom offset before limiting starts
await computePosition(reference, floating, {
middleware: [
shift({
limiter: limitShift({
offset: 10, // Allow 10px of shifting before limiting
mainAxis: true,
crossAxis: false
})
})
],
platform: domPlatform
});
// Dynamic offset based on state
await computePosition(reference, floating, {
middleware: [
shift({
limiter: limitShift({
offset: ({ rects }) => ({
mainAxis: rects.reference.width / 4,
crossAxis: 0
})
})
})
],
platform: domPlatform
});import { computePosition, offset, flip, shift } from "@floating-ui/core";
// Standard tooltip setup
const positionTooltip = async (reference, tooltip) => {
const { x, y, placement } = await computePosition(reference, tooltip, {
placement: "top",
middleware: [
offset(6), // 6px gap
flip(), // Flip if doesn't fit
shift({ padding: 8 }) // Shift with 8px padding
],
platform: domPlatform
});
// Apply positioning
Object.assign(tooltip.style, {
left: `${x}px`,
top: `${y}px`,
});
};import { computePosition, flip, shift, offset } from "@floating-ui/core";
// Dropdown with custom fallback strategy
const positionDropdown = async (trigger, dropdown) => {
const { x, y, placement } = await computePosition(trigger, dropdown, {
placement: "bottom-start",
middleware: [
offset(4),
flip({
fallbackPlacements: ["bottom-end", "top-start", "top-end"],
fallbackStrategy: "bestFit"
}),
shift({ padding: 16 })
],
platform: domPlatform
});
// Apply positioning
Object.assign(dropdown.style, {
left: `${x}px`,
top: `${y}px`,
});
};