Render Into a Different Part of the DOM Tree
Portals
`createPortal` lets a component render its children into a different DOM node, while still belonging to the React tree.
What you'll learn
- Render a modal into `document.body`
- Understand event bubbling through portals
A portal lets you render children into a DOM node somewhere
else on the page — typically document.body — while keeping them
in the same React tree.
Useful for modals, tooltips, dropdown menus, popovers — anything
that should float above the layout without being constrained by an
ancestor’s overflow: hidden or position.
The API
import { createPortal } from "react-dom";
function Modal({ children, onClose }) {
return createPortal(
<div className="modal-backdrop" onClick={onClose}>
<div className="modal" onClick={e => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
} Wherever in the tree you render <Modal>, the actual <div class="modal-backdrop"> ends up
as a direct child of <body> — escaping any restrictive parent.
React Tree vs DOM Tree
Even though the DOM is different, the React tree is unchanged:
- Context still works —
<ThemeContext.Provider>higher up still reaches into the portaled content - Events still bubble through the React tree — clicking inside
the modal triggers
onClickhandlers on its React ancestors, not its DOM ancestors
That last point is non-obvious. A click inside a portaled modal
bubbles to where you wrote <Modal> in your JSX — not to <body>.
A Pattern: A Dedicated Mount Node
Some apps add a dedicated <div id="portals"> next to <div id="root">
in index.html, and portal everything there:
<body>
<div id="root"></div>
<div id="portals"></div>
</body> createPortal(<Modal />, document.getElementById("portals")); Keeps portaled content separate from the main app DOM — easier to debug.
Up Next
Catching errors before they crash the whole app.
Error Boundaries →