or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-positioning.mdfocus-management.mdindex.mdinteraction-hooks.mdlayout-components.mdlist-navigation.mdpositioning-middleware.mdtransitions.mdtree-context.md
tile.json

positioning-middleware.mddocs/

Positioning Middleware

Re-exported positioning middleware from @floating-ui/react-dom for collision detection, placement optimization, visual enhancements, and advanced positioning behaviors.

Capabilities

Core Positioning Middleware

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>
    </>
  );
}

Visual Enhancement Middleware

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>
  );
}

Auto-Placement Middleware

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>
    </>
  );
}

Size Middleware

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 Functions

computePosition Function

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;
}

autoUpdate Function

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;
}

Utility Functions

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>>;
}

Common Middleware Patterns

Basic Tooltip

const middleware = [
  offset(10),
  flip(),
  shift({ padding: 5 }),
  arrow({ element: arrowRef }),
];

Dropdown Menu

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`,
      });
    },
  }),
];

Context Menu

const middleware = [
  flip({ fallbackStrategy: 'bestFit' }),
  shift({ padding: 10 }),
  hide({ strategy: 'referenceHidden' }),
];

Inline Tooltip

const middleware = [
  inline(),
  offset(8),
  flip(),
  shift({ padding: 5 }),
  hide(),
];

Type Definitions

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 };
}