Portal в Chakra UI используется для рендеринга
компонентов вне текущей иерархии DOM, что особенно важно для модальных
окон, тултипов, всплывающих уведомлений и других элементов, которые
должны отображаться поверх всего остального контента. Основное
преимущество Portals заключается в том, что они позволяют избежать
проблем с CSS-стилями родительских компонентов, такими
как overflow: hidden, z-index или
position: relative.
В Chakra UI Portal реализован как компонент-обёртка,
который автоматически помещает дочерние элементы в корень документа
(document.body), обеспечивая корректное отображение поверх
остальных компонентов.
Чтобы использовать Portal, необходимо импортировать компонент из Chakra UI:
import { Portal, Box, Button } from '@chakra-ui/react';
Простейший пример:
<Box position="relative" width="200px" height="200px" bg="blue.100">
Контент внутри Box
<Portal>
<Box position="absolute" top="0" left="0" bg="red.300" p={4}>
Контент в Portal
</Box>
</Portal>
</Box>
В этом примере красный Box будет отрисован в
document.body, а не внутри синего контейнера, что позволяет
избежать ограничений по позиционированию.
Chakra UI предоставляет минимальное количество свойств для
Portal, что делает его легким и удобным:
document.body.Пример с кастомным контейнером:
import { useRef } from 'react';
const CustomPortal = () => {
const containerRef = useRef();
return (
<>
<Box ref={containerRef} position="relative" height="300px" bg="green.50">
Контейнер для Portal
</Box>
<Portal containerRef={containerRef}>
<Box position="absolute" top="10px" left="10px" bg="yellow.300" p={2}>
Портал внутри кастомного контейнера
</Box>
</Portal>
</>
);
};
Portal часто применяют для реализации модальных
окон, тултипов и попапов.
Chakra UI автоматически использует Portal в своих компонентах
Modal, Popover, Tooltip, что
обеспечивает корректное отображение поверх остального интерфейса без
необходимости ручного управления z-index.
Пример модального окна с использованием Portal вручную:
import { useState } from 'react';
import { Box, Button, Portal } from '@chakra-ui/react';
const ManualModal = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onCl ick={() => setIsOpen(true)}>Открыть модальное окно</Button>
{isOpen && (
<Portal>
<Box
position="fixed"
top="0"
left="0"
width="100vw"
height="100vh"
bg="blackAlpha.600"
display="flex"
alignItems="center"
justifyContent="center"
>
<Box bg="white" p={6} borderRadius="md">
Содержимое модального окна
<Button mt={4} onCl ick={() => setIsOpen(false)}>
Закрыть
</Button>
</Box>
</Box>
</Portal>
)}
</>
);
};
Здесь Portal гарантирует, что модальное окно будет поверх всего интерфейса, не зависимо от родительских контейнеров и их стилей.
Chakra UI позволяет создавать несколько Portals одновременно. Это полезно, когда требуется рендерить несколько всплывающих элементов в разных частях страницы. Порядок рендеринга определяется порядком в DOM, а Chakra автоматически заботится о корректной вставке.
Пример нескольких динамических Portals:
import { useState } from 'react';
import { Portal, Box, Button } from '@chakra-ui/react';
const MultiplePortals = () => {
const [showFirst, setShowFirst] = useState(true);
const [showSecond, setShowSecond] = useState(true);
return (
<>
<Button onCl ick={() => setShowFirst(!showFirst)}>Toggle Первый Portal</Button>
<Button ml={2} onCl ick={() => setShowSecond(!showSecond)}>Toggle Второй Portal</Button>
{showFirst && (
<Portal>
<Box position="fixed" top="20px" left="20px" bg="red.300" p={2}>
Первый Portal
</Box>
</Portal>
)}
{showSecond && (
<Portal>
<Box position="fixed" bottom="20px" right="20px" bg="blue.300" p={2}>
Второй Portal
</Box>
</Portal>
)}
</>
);
};
Так как Portal рендерит элементы вне родительской иерархии, нужно учитывать:
motion.div из
framer-motion или Transition из Chakra UI для
плавного появления/исчезновения.useFocusLock, чтобы
пользователи не могли взаимодействовать с элементами позади Portal.Пример анимации с Chakra UI и Portal:
import { motion } from 'framer-motion';
import { Portal, Box } from '@chakra-ui/react';
const AnimatedPortal = () => (
<Portal>
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
>
<Box bg="purple.300" p={4} borderRadius="md">
Анимированный Portal
</Box>
</motion.div>
</Portal>
);
Modal, Tooltip, Popover, ручное
использование Portal не требуется.containerRef для контроля,
куда рендерится Portal.Portal в Chakra UI — мощный инструмент, который обеспечивает чистую структуру DOM, стабильное позиционирование и совместимость с анимациями и фокусом. Его грамотное использование позволяет создавать сложные интерфейсы без проблем с перекрытием и стилями.