Portal
Portal renders its children into a different part of the DOM — by default document.body —
while keeping them in the React tree where they are written. The escape hatch every overlay is
built on.
What it is
A wrapper over React's createPortal. The children stay in the component tree — props, context,
and state flow into them normally — but the DOM nodes are mounted somewhere else. That separation
is what lets a tooltip written inside a clipped card still escape the card's overflow: hidden.
Portal is SSR-safe: with no document available, it renders null and mounts on the client once
the document exists.
Install
Portal is exported from usemotif. No separate install.
API
Portal
stablefunction Portal(props: PortalProps): JSX.Element | null
The content to render at the target. Stays in the React tree where written — only the DOM placement moves.
The DOM node to render into. Defaults to `document.body`. Pass an element for a custom target — a shadow-DOM root, a layout-specific layer.
Accessibility
A portal moves DOM nodes, and that can break the reading order. Screen readers and the Tab
sequence follow the DOM, not the React tree — content portalled to document.body is announced
and tabbed to at the end of the page, far from the control that opened it.
For a modal this is usually right: focus moves into the portalled content deliberately, managed
by a FocusScope. For a tooltip or a popover tied to a specific
control, it can be wrong — the user tabs past the trigger and the related content is nowhere near
it. When you portal interactive content, manage focus explicitly and consider aria-owns to
restore the logical relationship.
Examples
A toast rendered at the body, clear of every ancestor:
Portal into a custom layer element: