Portal

Renders into another DOM element. Renders nothing when the target is missing, so the usual null check disappears.

API

Props

proptypedefault
element?Element | nullnullthe DOM node to render into
children?Renderablerendered inside the target element

Examples

Usage

It is React's own createPortal with the null check folded in. The children leave the DOM position of their parent and land inside the target element, while staying exactly where they were in the React tree: context still reaches them, and events still bubble to their React parent rather than to their new DOM ancestor.

tsx
<Portal element={document.querySelector("[data-root]")}>
  <Cursor />
</Portal>

Details

The missing target

A null target renders nothing instead of throwing. This is what makes the primitive usable during server rendering and on the first client render, when document is not there yet or the anchor has not mounted: the Portal renders empty, and fills in as soon as the element exists.

Because of that, prefer a target held in state over one read inline. A querySelector evaluated during render returns null on the server and a node on the client, which is exactly the shape of a hydration mismatch; a callback ref sets state after mount and re-renders the Portal once, deterministically.

tsx
const [anchor, setAnchor] = useState<Element | null>(null);

<div ref={setAnchor} />
<Portal element={anchor}>
  <Tooltip />
</Portal>

children is a Renderable

Since the children may never render — no target, no output — they are a Renderable: pass a component and its body stays unevaluated until the target exists.