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

layout-components.mddocs/

Layout Components

Essential components for controlling floating element layout, portaling to different DOM locations, overlays for modal behavior, and visual presentation enhancements.

Capabilities

FloatingPortal Component

Portals floating elements to a specified container or creates a new container, with optional tab order preservation.

/**
 * Portals floating elements to specified container
 * @param props - Portal configuration
 * @returns Portal component for DOM placement
 */
interface FloatingPortalProps {
  children?: React.ReactNode;
  id?: string;
  root?: HTMLElement | ShadowRoot | null | React.MutableRefObject<HTMLElement | ShadowRoot | null>;
  preserveTabOrder?: boolean;
}

declare const FloatingPortal: React.FC<FloatingPortalProps>;

Usage Examples:

import { FloatingPortal, useFloating } from '@floating-ui/react';
import { useState } from 'react';

// Basic portal to document.body
function BasicPortal() {
  const [isOpen, setIsOpen] = useState(false);
  const { refs, floatingStyles } = useFloating();

  return (
    <>
      <button ref={refs.setReference} onClick={() => setIsOpen(!isOpen)}>
        Toggle
      </button>
      {isOpen && (
        <FloatingPortal>
          <div
            ref={refs.setFloating}
            style={{
              ...floatingStyles,
              background: 'white',
              border: '1px solid black',
              padding: '8px',
            }}
          >
            Portaled content
          </div>
        </FloatingPortal>
      )}
    </>
  );
}

// Portal to custom container
function CustomPortal() {
  const [isOpen, setIsOpen] = useState(false);
  const [containerRef, setContainerRef] = useState<HTMLElement | null>(null);
  const { refs, floatingStyles } = useFloating();

  return (
    <>
      <div ref={setContainerRef} style={{ position: 'relative' }}>
        Custom container
      </div>
      <button ref={refs.setReference} onClick={() => setIsOpen(!isOpen)}>
        Toggle
      </button>
      {isOpen && (
        <FloatingPortal root={containerRef}>
          <div
            ref={refs.setFloating}
            style={{
              ...floatingStyles,
              background: 'lightblue',
              padding: '8px',
            }}
          >
            Content in custom container
          </div>
        </FloatingPortal>
      )}
    </>
  );
}

// Portal with tab order preservation
function TabOrderPortal() {
  const [isOpen, setIsOpen] = useState(false);
  const { refs, floatingStyles } = useFloating();

  return (
    <>
      <button>Before</button>
      <button ref={refs.setReference} onClick={() => setIsOpen(!isOpen)}>
        Toggle Portal
      </button>
      <button>After</button>
      {isOpen && (
        <FloatingPortal preserveTabOrder>
          <div
            ref={refs.setFloating}
            style={{
              ...floatingStyles,
              background: 'white',
              border: '1px solid black',
              padding: '8px',
            }}
          >
            <button>Portal Button 1</button>
            <button>Portal Button 2</button>
          </div>
        </FloatingPortal>
      )}
    </>
  );
}

FloatingOverlay Component

Creates a fixed overlay behind floating elements for modal behavior and visual separation.

/**
 * Fixed overlay for modal behavior and visual separation
 * @param props - Overlay configuration
 * @returns Overlay component for modal backgrounds
 */
interface FloatingOverlayProps {
  children?: React.ReactNode;
  lockScroll?: boolean;
  style?: React.CSSProperties;
  className?: string;
}

declare const FloatingOverlay: React.FC<FloatingOverlayProps>;

Usage Examples:

import { 
  FloatingOverlay, 
  FloatingPortal, 
  FloatingFocusManager,
  useFloating,
  useClick,
  useDismiss,
  useInteractions
} from '@floating-ui/react';
import { useState } from 'react';

// Modal dialog with overlay
function ModalDialog() {
  const [isOpen, setIsOpen] = useState(false);

  const { refs, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
  });

  const click = useClick(context);
  const dismiss = useDismiss(context, {
    outsidePress: false, // Prevent closing on overlay click
  });

  const { getReferenceProps, getFloatingProps } = useInteractions([
    click,
    dismiss,
  ]);

  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Open Modal
      </button>
      {isOpen && (
        <FloatingPortal>
          <FloatingOverlay 
            lockScroll
            style={{ background: 'rgba(0, 0, 0, 0.5)' }}
          >
            <FloatingFocusManager context={context} modal>
              <div
                ref={refs.setFloating}
                {...getFloatingProps()}
                style={{
                  position: 'fixed',
                  top: '50%',
                  left: '50%',
                  transform: 'translate(-50%, -50%)',
                  background: 'white',
                  border: '1px solid black',
                  borderRadius: '8px',
                  padding: '1rem',
                  maxWidth: '400px',
                }}
              >
                <h2>Modal Title</h2>
                <p>Modal content goes here.</p>
                <button onClick={() => setIsOpen(false)}>Close</button>
              </div>
            </FloatingFocusManager>
          </FloatingOverlay>
        </FloatingPortal>
      )}
    </>
  );
}

// Clickable overlay to close
function ClickableOverlay() {
  const [isOpen, setIsOpen] = useState(false);
  const { refs } = useFloating();

  return (
    <>
      <button ref={refs.setReference} onClick={() => setIsOpen(!isOpen)}>
        Open with Clickable Overlay
      </button>
      {isOpen && (
        <FloatingPortal>
          <FloatingOverlay 
            lockScroll
            style={{ background: 'rgba(0, 0, 0, 0.3)' }}
            onClick={() => setIsOpen(false)}
          >
            <div
              ref={refs.setFloating}
              onClick={(e) => e.stopPropagation()} // Prevent overlay close
              style={{
                position: 'fixed',
                top: '20%',
                left: '50%',
                transform: 'translateX(-50%)',
                background: 'white',
                padding: '1rem',
                borderRadius: '4px',
              }}
            >
              <p>Click overlay to close</p>
            </div>
          </FloatingOverlay>
        </FloatingPortal>
      )}
    </>
  );
}

// Custom styled overlay
function CustomOverlay() {
  const [isOpen, setIsOpen] = useState(false);
  const { refs } = useFloating();

  return (
    <>
      <button ref={refs.setReference} onClick={() => setIsOpen(!isOpen)}>
        Custom Overlay
      </button>
      {isOpen && (
        <FloatingPortal>
          <FloatingOverlay 
            className="custom-overlay"
            style={{
              background: 'linear-gradient(45deg, rgba(255,0,0,0.1), rgba(0,0,255,0.1))',
              backdropFilter: 'blur(2px)',
            }}
          >
            <div
              ref={refs.setFloating}
              style={{
                position: 'fixed',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                background: 'white',
                padding: '2rem',
                borderRadius: '12px',
                boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1)',
              }}
            >
              <p>Custom styled overlay</p>
              <button onClick={() => setIsOpen(false)}>Close</button>
            </div>
          </FloatingOverlay>
        </FloatingPortal>
      )}
    </>
  );
}

FloatingArrow Component

SVG arrow component that points from the floating element to its reference element.

/**
 * SVG arrow pointing from floating element to reference
 * @param props - Arrow configuration
 * @returns SVG arrow component
 */
interface FloatingArrowProps {
  context: FloatingContext;
  width?: number;
  height?: number;
  tipRadius?: number;
  staticOffset?: string | number | null;
  strokeWidth?: number;
  stroke?: string;
  fill?: string;
  d?: string;
  style?: React.CSSProperties;
  className?: string;
}

declare const FloatingArrow: React.FC<FloatingArrowProps>;

Usage Examples:

import { 
  FloatingArrow, 
  useFloating, 
  useHover, 
  useInteractions,
  arrow,
  offset,
  flip,
  shift
} from '@floating-ui/react';
import { useState, useRef } from 'react';

// Basic arrow tooltip
function ArrowTooltip() {
  const [isOpen, setIsOpen] = useState(false);
  const arrowRef = useRef<SVGSVGElement>(null);

  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [
      offset(10),
      flip(),
      shift({ padding: 5 }),
      arrow({ element: arrowRef }),
    ],
  });

  const hover = useHover(context);
  const { getReferenceProps, getFloatingProps } = useInteractions([hover]);

  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Hover for tooltip
      </button>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={{
            ...floatingStyles,
            background: 'black',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '4px',
            fontSize: '14px',
          }}
          {...getFloatingProps()}
        >
          Tooltip with arrow
          <FloatingArrow 
            ref={arrowRef} 
            context={context} 
            fill="black"
          />
        </div>
      )}
    </>
  );
}

// Custom styled arrow
function CustomArrow() {
  const [isOpen, setIsOpen] = useState(false);
  const arrowRef = useRef<SVGSVGElement>(null);

  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [
      offset(15),
      arrow({ element: arrowRef }),
    ],
  });

  const hover = useHover(context);
  const { getReferenceProps, getFloatingProps } = useInteractions([hover]);

  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Custom Arrow
      </button>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={{
            ...floatingStyles,
            background: 'lightblue',
            border: '2px solid darkblue',
            padding: '12px',
            borderRadius: '8px',
          }}
          {...getFloatingProps()}
        >
          Custom arrow styling
          <FloatingArrow 
            ref={arrowRef} 
            context={context}
            width={16}
            height={8}
            tipRadius={2}
            fill="lightblue"
            stroke="darkblue"
            strokeWidth={2}
          />
        </div>
      )}
    </>
  );
}

// Arrow with different shapes
function CustomShapeArrow() {
  const [isOpen, setIsOpen] = useState(false);
  const arrowRef = useRef<SVGSVGElement>(null);

  const { refs, floatingStyles, context } = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [offset(12), arrow({ element: arrowRef })],
  });

  const hover = useHover(context);
  const { getReferenceProps, getFloatingProps } = useInteractions([hover]);

  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Custom Shape Arrow
      </button>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={{
            ...floatingStyles,
            background: 'purple',
            color: 'white',
            padding: '10px',
            borderRadius: '6px',
          }}
          {...getFloatingProps()}
        >
          Custom arrow shape
          <FloatingArrow 
            ref={arrowRef} 
            context={context}
            d="M0,0 L0,8 L8,4 z" // Custom path for arrow shape
            fill="purple"
          />
        </div>
      )}
    </>
  );
}

Portal Context Hooks

useFloatingPortalNode Hook

Creates or accesses a portal node for floating elements.

/**
 * Creates or accesses portal node for floating elements
 * @param props - Portal node configuration
 * @returns Portal node and setters
 */
function useFloatingPortalNode(
  props?: UseFloatingPortalNodeProps
): {
  portalNode: HTMLElement | null;
  setPortalNode: React.Dispatch<React.SetStateAction<HTMLElement | null>>;
};

interface UseFloatingPortalNodeProps {
  id?: string;
  enabled?: boolean;
}

usePortalContext Hook

Accesses the current portal context and configuration.

/**
 * Access current portal context
 * @returns Portal context information
 */
function usePortalContext(): {
  portalNode: HTMLElement | null;
  setPortalNode: React.Dispatch<React.SetStateAction<HTMLElement | null>>;
  preserveTabOrder?: boolean;
} | null;

Usage Example:

import { useFloatingPortalNode, usePortalContext } from '@floating-ui/react';

function CustomPortalComponent() {
  const { portalNode, setPortalNode } = useFloatingPortalNode({
    id: 'custom-portal',
  });

  const portalContext = usePortalContext();

  return (
    <div>
      <p>Portal node: {portalNode?.id}</p>
      <p>Context available: {portalContext ? 'Yes' : 'No'}</p>
    </div>
  );
}

Layout Patterns

Modal Layouts

<FloatingPortal>
  <FloatingOverlay lockScroll>
    <FloatingFocusManager context={context} modal>
      {/* Modal content */}
    </FloatingFocusManager>
  </FloatingOverlay>
</FloatingPortal>

Tooltip Layouts

<FloatingPortal>
  <div style={floatingStyles}>
    {/* Tooltip content */}
    <FloatingArrow ref={arrowRef} context={context} />
  </div>
</FloatingPortal>

Dropdown Layouts

<FloatingPortal preserveTabOrder>
  <div style={floatingStyles}>
    {/* Dropdown content */}
  </div>
</FloatingPortal>

Styling Considerations

Z-Index Management

Portals automatically handle z-index stacking by rendering to document.body or specified containers.

CSS Custom Properties

Components support CSS custom properties for theming:

.floating-overlay {
  --overlay-background: rgba(0, 0, 0, 0.5);
  --overlay-backdrop-filter: blur(4px);
}

Responsive Design

Layout components work with responsive middleware and viewport detection for optimal mobile and desktop experiences.