Chapter 1 of 4
What a Portal Is
Rendering into a different DOM node while staying in the React tree.
createPortal renders children into a DOM node somewhere else in the document, while keeping them exactly where they are in the React tree.
import { createPortal } from "react-dom";
function Modal({ children }) {
return createPortal(children, document.body);
}Why you need one
A modal or tooltip nested deep inside the page inherits its ancestors' CSS. overflow: hidden clips it, a transform traps position: fixed, and a low z-index on an ancestor puts it behind other content. No amount of styling on the modal itself can escape those.
overflow: hiddenon any ancestor clips the popup.- A
transformorfilteron an ancestor makes it the containing block forposition: fixed. - Stacking contexts mean a child can never rise above a sibling of its ancestor.
Rendering into document.body sidesteps all three.
Events still bubble through React
This is the part that surprises people. A portal's DOM node is elsewhere, but events bubble up the React tree, not the DOM tree - so a click inside a portal reaches handlers on its React parent.
function Panel() {
return (
<div onClick={() => console.log("parent heard the click")}>
<Modal>
<button>Click me</button>
</Modal>
</div>
);
}
// Clicking the button logs "parent heard the click",
// even though the button is a child of document.bodyContext works too
Because the component stays in the React tree, it keeps access to every context and every error boundary above it. Portals change the DOM position and nothing else.