CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-floating-ui--react

React library for creating accessible floating UI elements like tooltips, popovers, and dropdowns with advanced positioning

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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

docs

core-positioning.md

focus-management.md

index.md

interaction-hooks.md

layout-components.md

list-navigation.md

positioning-middleware.md

transitions.md

tree-context.md

tile.json