or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

auto-update.mdindex.mdmiddleware.mdplatform.md
tile.json

middleware.mddocs/

Positioning Middleware

Floating UI DOM provides a comprehensive set of middleware functions that modify positioning behavior and provide positioning data. Middleware functions are composable and can be combined to create sophisticated positioning logic.

Capabilities

Offset Middleware

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.

/**
 * Modifies placement by translating floating element along specified axes
 * @param options - Offset configuration or number for mainAxis offset
 * @returns Middleware instance
 */
function offset(options?: OffsetOptions): Middleware;

type OffsetOptions = OffsetValue | Derivable<OffsetValue>;

type OffsetValue = number | {
  /**
   * The axis that runs along the side of the floating element. Represents
   * the distance (gutter or margin) between the reference and floating element.
   * @default 0
   */
  mainAxis?: number;
  /**
   * The axis that runs along the alignment of the floating element.
   * Represents the skidding between the reference and floating element.
   * @default 0
   */
  crossAxis?: number;
  /**
   * The same axis as `crossAxis` but applies only to aligned placements
   * and inverts the `end` alignment. When set to a number, it overrides the
   * `crossAxis` value.
   * @default null
   */
  alignmentAxis?: number | null;
};

Usage Examples:

import { computePosition, offset } from "@floating-ui/dom";

// Simple 10px offset
await computePosition(reference, floating, {
  middleware: [offset(10)]
});

// Detailed offset configuration
await computePosition(reference, floating, {
  middleware: [
    offset({
      mainAxis: 10,     // 10px gap from reference
      crossAxis: 5,     // 5px skidding
      alignmentAxis: -5 // 5px towards start alignment
    })
  ]
});

// Dynamic offset based on state
await computePosition(reference, floating, {
  middleware: [
    offset((state) => {
      return state.placement.includes('top') ? 15 : 10;
    })
  ]
});

Flip Middleware

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. Alternative to autoPlacement.

/**
 * Optimizes visibility by flipping placement to keep in view
 * @param options - Flip configuration options
 * @returns Middleware instance
 */
function flip(options?: FlipOptions | Derivable<FlipOptions>): Middleware;

interface FlipOptions extends DetectOverflowOptions {
  /**
   * The axis that runs along the side of the floating element. Determines
   * whether overflow along this axis is checked to perform a flip.
   * @default true
   */
  mainAxis?: boolean;
  /**
   * The axis that runs along the alignment of the floating element. Determines
   * whether overflow along this axis is checked to perform a flip.
   * - `true`: Whether to check cross axis overflow for both side and alignment flipping.
   * - `false`: Whether to disable all cross axis overflow checking.
   * - `'alignment'`: Whether to check cross axis overflow for alignment flipping only.
   * @default true
   */
  crossAxis?: boolean | 'alignment';
  /**
   * Placements to try sequentially if the preferred placement does not fit.
   * @default [oppositePlacement] (computed)
   */
  fallbackPlacements?: Array<Placement>;
  /**
   * What strategy to use when no placements fit.
   * @default 'bestFit'
   */
  fallbackStrategy?: 'bestFit' | 'initialPlacement';
  /**
   * Whether to allow fallback to the perpendicular axis of the preferred
   * placement, and if so, which side direction along the axis to prefer.
   * @default 'none' (disallow fallback)
   */
  fallbackAxisSideDirection?: 'none' | 'start' | 'end';
  /**
   * Whether to flip to placements with the opposite alignment if they fit better.
   * @default true
   */
  flipAlignment?: boolean;
}

Usage Examples:

import { computePosition, flip } from "@floating-ui/dom";

// Basic flip behavior
await computePosition(reference, floating, {
  placement: 'bottom',
  middleware: [flip()]
});

// Custom fallback placements
await computePosition(reference, floating, {
  placement: 'bottom',
  middleware: [
    flip({
      fallbackPlacements: ['top', 'right', 'left'],
      fallbackStrategy: 'bestFit'
    })
  ]
});

// Flip only on main axis
await computePosition(reference, floating, {
  middleware: [
    flip({
      mainAxis: true,
      crossAxis: false
    })
  ]
});

Shift Middleware

Optimizes the visibility of the floating element by shifting it in order to keep it in view when it will overflow the clipping boundary.

/**
 * Optimizes visibility by shifting to keep in view when overflowing
 * @param options - Shift configuration options
 * @returns Middleware instance
 */
function shift(options?: ShiftOptions | Derivable<ShiftOptions>): Middleware;

interface ShiftOptions extends DetectOverflowOptions {
  /**
   * The axis that runs along the alignment of the floating element.
   * Determines whether to shift along this axis.
   * @default true
   */
  mainAxis?: boolean;
  /**
   * The axis that runs along the side of the floating element.
   * Determines whether to shift along this axis.
   * @default false
   */
  crossAxis?: boolean;
  /**
   * Accepts a limiter object that limits the shifting done in order to prevent
   * detachment.
   */
  limiter?: {
    fn: (state: MiddlewareState) => Coords;
    options?: any;
  };
}

Usage Examples:

import { computePosition, shift, limitShift } from "@floating-ui/dom";

// Basic shift behavior
await computePosition(reference, floating, {
  middleware: [shift()]
});

// Shift with padding and limiter
await computePosition(reference, floating, {
  middleware: [
    shift({
      padding: 10,
      limiter: limitShift()
    })
  ]
});

// Shift only on cross axis
await computePosition(reference, floating, {
  middleware: [
    shift({
      mainAxis: false,
      crossAxis: true
    })
  ]
});

Auto Placement Middleware

Optimizes the visibility of the floating element by choosing the placement that has the most space available automatically, without needing to specify a preferred placement. Alternative to flip.

/**
 * Optimizes visibility by choosing placement with most space automatically
 * @param options - Auto placement configuration options
 * @returns Middleware instance
 */
function autoPlacement(options?: AutoPlacementOptions | Derivable<AutoPlacementOptions>): Middleware;

interface AutoPlacementOptions extends DetectOverflowOptions {
  /**
   * The axis that runs along the alignment of the floating element.
   * Determines whether to check alignment axis overflow.
   * @default false
   */
  crossAxis?: boolean;
  /**
   * Choose placements with a particular alignment.
   * @default undefined
   */
  alignment?: Alignment;
  /**
   * Which placements are allowed to be chosen automatically.
   * @default allPlacements (computed)
   */
  allowedPlacements?: Array<Placement>;
}

Size Middleware

Provides data that allows you to change the size of the floating element — for instance, prevent it from overflowing the clipping boundary or match the width of the reference element.

/**
 * Provides data to change floating element size
 * @param options - Size configuration options
 * @returns Middleware instance
 */
function size(options?: SizeOptions | Derivable<SizeOptions>): Middleware;

interface SizeOptions extends DetectOverflowOptions {
  /**
   * Function that is called to perform style mutations to the floating element
   * to change its size.
   * @default undefined
   */
  apply?(args: MiddlewareState & {
    availableWidth: number;
    availableHeight: number;
  }): Promise<void>;
}

Usage Examples:

import { computePosition, size } from "@floating-ui/dom";

// Prevent overflow by resizing
await computePosition(reference, floating, {
  middleware: [
    size({
      apply({ availableWidth, availableHeight, elements }) {
        Object.assign(elements.floating.style, {
          maxWidth: `${availableWidth}px`,
          maxHeight: `${availableHeight}px`,
        });
      },
    })
  ]
});

// Match reference width
await computePosition(reference, floating, {
  middleware: [
    size({
      apply({ rects, elements }) {
        Object.assign(elements.floating.style, {
          width: `${rects.reference.width}px`,
        });
      },
    })
  ]
});

Hide Middleware

Provides data to hide the floating element in applicable situations, such as when it is not in the same clipping context as the reference element.

/**
 * Provides data to hide floating element when not in same clipping context
 * @param options - Hide configuration options
 * @returns Middleware instance
 */
function hide(options?: HideOptions | Derivable<HideOptions>): Middleware;

interface HideOptions extends DetectOverflowOptions {
  /**
   * The strategy used to determine when to hide the floating element.
   * @default 'referenceHidden'
   */
  strategy?: 'referenceHidden' | 'escaped';
}

Usage Examples:

import { computePosition, hide } from "@floating-ui/dom";

const { middlewareData } = await computePosition(reference, floating, {
  middleware: [hide()]
});

// Hide the floating element based on middleware data
if (middlewareData.hide?.referenceHidden) {
  floating.style.visibility = 'hidden';
} else {
  floating.style.visibility = 'visible';
}

Arrow Middleware

Provides data to position an inner element of the floating element so that it appears centered to the reference element.

/**
 * Positions inner element to appear centered to reference element
 * @param options - Arrow configuration options
 * @returns Middleware instance
 */
function arrow(options: ArrowOptions | Derivable<ArrowOptions>): Middleware;

interface ArrowOptions {
  /**
   * The arrow element to be positioned.
   */
  element: Element;
  /**
   * The padding between the arrow element and the edges of the floating element.
   * @default 0
   */
  padding?: Padding;
}

type Padding = number | Partial<SideObject>;

Usage Examples:

import { computePosition, arrow, offset } from "@floating-ui/dom";

const arrowElement = document.querySelector('#arrow');

const { x, y, placement, middlewareData } = await computePosition(reference, floating, {
  middleware: [
    offset(8),
    arrow({ element: arrowElement })
  ]
});

// Position the floating element
Object.assign(floating.style, {
  left: `${x}px`,
  top: `${y}px`,
});

// Position the arrow
const { x: arrowX, y: arrowY } = middlewareData.arrow;
const side = placement.split('-')[0];

const arrowSide = {
  top: 'bottom',
  right: 'left',
  bottom: 'top',
  left: 'right',
}[side];

Object.assign(arrowElement.style, {
  left: arrowX != null ? `${arrowX}px` : '',
  top: arrowY != null ? `${arrowY}px` : '',
  [arrowSide]: '-4px',
});

Inline Middleware

Provides improved positioning for inline reference elements that can span over multiple lines, such as hyperlinks or range selections.

/**
 * Improved positioning for inline reference elements spanning multiple lines
 * @param options - Inline configuration options
 * @returns Middleware instance
 */
function inline(options?: InlineOptions | Derivable<InlineOptions>): Middleware;

interface InlineOptions {
  /**
   * Viewport-relative `x` coordinate to choose a `ClientRect`.
   * @default undefined
   */
  x?: number;
  /**
   * Viewport-relative `y` coordinate to choose a `ClientRect`.
   * @default undefined
   */
  y?: number;
  /**
   * Represents the padding around a disjointed rect when choosing it.
   * @default 2
   */
  padding?: Padding;
}

Utility Functions

Detect Overflow

Resolves with an object of overflow side offsets that determine how much the element is overflowing a given clipping boundary on each side.

/**
 * Resolves overflow side offsets for clipping boundary detection
 * @param state - Current middleware state
 * @param options - Overflow detection options
 * @returns Promise resolving to side overflow measurements
 */
function detectOverflow(
  state: MiddlewareState,
  options?: DetectOverflowOptions | Derivable<DetectOverflowOptions>
): Promise<SideObject>;

interface DetectOverflowOptions {
  /**
   * The clipping boundary area.
   * @default 'clippingAncestors'
   */
  boundary?: Boundary;
  /**
   * The root clipping boundary area.
   * @default 'viewport'
   */
  rootBoundary?: RootBoundary;
  /**
   * Virtual padding around the boundary to check for overflow.
   * @default 0
   */
  padding?: Padding;
  /**
   * Whether to check for overflow using the alternate element's boundary.
   * @default false
   */
  altBoundary?: boolean;
  /**
   * Use the reference element as the clipping boundary.
   * @default false
   */
  elementContext?: ElementContext;
}

Limit Shift

Built-in limiter that will stop shift() at a certain point.

/**
 * Built-in limiter to stop shift() at certain point
 * @param options - Limit shift configuration options
 * @returns Limiter function for use with shift middleware
 */
function limitShift(options?: LimitShiftOptions | Derivable<LimitShiftOptions>): {
  options: any;
  fn: (state: MiddlewareState) => Coords;
};

interface LimitShiftOptions {
  /**
   * Offset when the floating element should stop shifting to prevent
   * detachment. This essentially stops the floating element from shifting
   * further than its reference element's edge.
   * @default 0
   */
  offset?: number | Partial<SideObject> | ((args: {
    placement: Placement;
    reference: Rect;
    floating: Rect;
  }) => number | Partial<SideObject>);
  /**
   * The axis that runs along the alignment of the floating element.
   * Determines whether to limit shifting along this axis.
   * @default true
   */
  mainAxis?: boolean;
  /**
   * The axis that runs along the side of the floating element.
   * Determines whether to limit shifting along this axis.
   * @default true
   */
  crossAxis?: boolean;
}

Common Middleware Patterns

Basic Tooltip

import { computePosition, offset, flip, shift } from "@floating-ui/dom";

await computePosition(reference, floating, {
  placement: 'top',
  middleware: [
    offset(8),
    flip(),
    shift({ padding: 5 })
  ]
});

Dropdown Menu

import { computePosition, autoPlacement, size, hide } from "@floating-ui/dom";

await computePosition(reference, floating, {
  middleware: [
    autoPlacement({
      allowedPlacements: ['bottom-start', 'top-start']
    }),
    size({
      apply({ availableHeight, elements }) {
        Object.assign(elements.floating.style, {
          maxHeight: `${availableHeight}px`,
          overflow: 'auto'
        });
      }
    }),
    hide()
  ]
});

Popover with Arrow

import { computePosition, offset, flip, shift, arrow } from "@floating-ui/dom";

const arrowElement = floating.querySelector('.arrow');

await computePosition(reference, floating, {
  placement: 'bottom',
  middleware: [
    offset(8),
    flip(),
    shift({ padding: 5 }),
    arrow({ element: arrowElement })
  ]
});