FloatingPortal компонент

FloatingPortal — это компонент из библиотеки Floating UI, который служит для рендеринга всплывающих элементов вне обычного DOM-контекста родителя. Это особенно важно, когда всплывающий элемент (например, tooltip, popover, dropdown) не должен быть ограничен стилями родительских контейнеров, такими как overflow: hidden или z-index.


Основные концепции

  • Порталы позволяют рендерить компонент в другом месте DOM, чаще всего в body.
  • FloatingPortal обеспечивает, чтобы все плавающие элементы имели корректное позиционирование независимо от контекста, в котором они созданы.
  • Используется вместе с FloatingFocusManager, FloatingArrow и другими утилитами Floating UI для полноценного управления всплывающими элементами.

Установка и импорт

import { FloatingPortal } from '@floating-ui/react-dom-interactions';

FloatingPortal не требует отдельной установки, если используется основной пакет Floating UI для React (@floating-ui/react-dom-interactions).


Базовое использование

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

function TooltipExample() {
  const [open, setOpen] = useState(false);
  const { x, y, reference, floating, strategy } = useFloating({
    placement: 'top'
  });

  return (
    <>
      <button ref={reference} onMouseEn ter={() => setOpen(true)} onMouseLe ave={() => setOpen(false)}>
        Hover me
      </button>

      {open && (
        <FloatingPortal>
          <div
            ref={floating}
            style={{
              position: strategy,
              top: y ?? 0,
              left: x ?? 0,
              background: 'black',
              color: 'white',
              padding: '5px 10px',
              borderRadius: '4px'
            }}
          >
            Tooltip content
          </div>
        </FloatingPortal>
      )}
    </>
  );
}

Ключевые моменты:

  • Всплывающий элемент рендерится в <FloatingPortal> вместо обычного родителя.
  • strategy управляет типом позиционирования: absolute или fixed.
  • x и y — вычисленные координаты, определяемые Floating UI.

Использование нескольких порталов

FloatingPortal поддерживает множественные всплывающие элементы в одной странице. Каждый портал может быть независимым:

<FloatingPortal id="dropdown-portal">
  <DropdownMenu />
</FloatingPortal>

<FloatingPortal id="tooltip-portal">
  <Tooltip />
</FloatingPortal>

Параметр id позволяет явно управлять конкретным порталом и предотвращает конфликт с другими порталами на странице.


Контроль контейнера

По умолчанию FloatingPortal рендерит свои элементы в document.body. При необходимости можно указать кастомный контейнер:

<FloatingPortal root={document.getElementById('custom-root')}>
  <PopoverContent />
</FloatingPortal>
  • root — любой DOM-элемент, в который будет помещён портал.
  • Это полезно для интеграции с фреймворками, где нужен контроль над деревом DOM.

Взаимодействие с анимацией

FloatingPortal полностью совместим с библиотеками анимации (например, Framer Motion). Основное правило — анимировать дочерние элементы портала, а не сам портал. Пример:

import { motion } from 'framer-motion';

<FloatingPortal>
  <motion.div
    ref={floating}
    initial={{ opacity: 0, scale: 0.95 }}
    animate={{ opacity: 1, scale: 1 }}
    exit={{ opacity: 0, scale: 0.95 }}
    style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}
  >
    Animated Popover
  </motion.div>
</FloatingPortal>

Практические рекомендации

  • Использовать FloatingPortal всегда, когда всплывающий элемент должен выходить за пределы родительского контейнера.
  • Для динамически создаваемых элементов использовать id или root для управления несколькими порталами.
  • Для комплексных всплывающих интерфейсов сочетать с FloatingFocusManager для контроля фокуса и клавиатурной навигации.
  • Необходимо учитывать, что рендеринг через портал может влиять на контекст стилей: CSS-переменные, наследуемые свойства и z-index могут потребовать корректировок.

Примеры сочетания с другими компонентами

Popover с стрелкой и фокусом:

import { FloatingPortal, useFloating, FloatingArrow, FloatingFocusManager } from '@floating-ui/react-dom-interactions';

<FloatingFocusManager context={context}>
  <FloatingPortal>
    <div ref={floating} style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}>
      Popover Content
      <FloatingArrow ref={arrowRef} />
    </div>
  </FloatingPortal>
</FloatingFocusManager>
  • FloatingFocusManager обеспечивает правильное управление фокусом при открытии и закрытии портала.
  • FloatingArrow позволяет визуально указать на элемент-источник.

FloatingPortal в Floating UI — это надежный инструмент для управления всплывающими элементами вне обычного DOM-контекста, обеспечивающий гибкость позиционирования, совместимость с анимацией и удобное управление несколькими порталами на одной странице.