React: Portals
Normally, React components render their JSX inside the DOM node of their parent component. The deeper a component is nested in the React tree, the deeper it is nested in the actual HTML DOM.
What are React Portals, and what common UI problem do they solve?
1. The Problem: z-index and overflow: hidden
Imagine you have a deeply nested <Button /> inside a scrollable sidebar. When the user clicks the button, you want to render a <Modal /> popup over the entire screen.
function Sidebar() {
return (
<div className="sidebar" style={{ overflow: 'hidden' }}>
<Button />
{/* If isModalOpen is true, render the modal HERE in the DOM */}
<Modal />
</div>
);
}If the Modal is rendered directly inside the Sidebar, it is bound by the CSS rules of the Sidebar.
- Because the Sidebar has
overflow: hidden, the Modal will be chopped off and trapped inside the tiny sidebar box. - Even if you apply
position: fixedandz-index: 9999to the Modal, it can still be visually trapped by conflicting stacking contexts from parent elements.
2. The Solution: createPortal
React Portals provide a first-class way to render a component's HTML payload into a completely different part of the DOM, while keeping the component itself deeply integrated in the React state tree.
Instead of rendering the Modal inside the Sidebar's DOM node, we can "teleport" it directly to the bottom of the <body> tag, bypassing all parent CSS constraints.
How to use createPortal:
import { createPortal } from 'react-dom';
function Modal({ children }) {
// 1. Select the destination DOM node (usually defined in index.html)
const portalRoot = document.getElementById('modal-root');
// 2. Teleport the JSX payload to that node
return createPortal(
<div className="modal-overlay">
<div className="modal-content">
{children}
</div>
</div>,
portalRoot
);
}Now, when you use <Modal /> inside your deeply nested Sidebar component, React handles the state and props normally, but physically attaches the HTML to <div id="modal-root"></div>.
3. Event Bubbling in Portals
A common interview trap involves Event Bubbling.
If a Portal renders its HTML outside the parent DOM node, do events still bubble up to the React parent?
Yes! React relies on the Virtual DOM hierarchy for event bubbling, not the physical HTML DOM. If a user clicks inside the Portal, the onClick event will bubble up to the <Sidebar /> component that initiated it, allowing you to catch events normally.
Senior-Level Interview Answer
React Portals, exposed via
ReactDOM.createPortal(), allow developers to render a component's visual DOM payload into an entirely different DOM node outside the parent component's physical hierarchy. This is fundamentally required for constructing global overlays like Modals, Tooltips, and Dropdowns. Without Portals, these overlay components would fall victim to their parents' CSS stacking contexts, specificallyoverflow: hiddenboundaries and conflictingz-indexhierarchies. Crucially, while a Portal physically detaches the element in the HTML DOM, it remains perfectly integrated in React's Virtual DOM tree. This means React contexts, state updates, and synthetic event bubbling operate exactly as if the component were rendered in-place.
Common Interview Mistakes
❌ Creating the target DOM node inside the component
A mistake developers make is trying to use document.createElement('div') inside the functional component without properly managing it with useEffect. The target destination (like document.body or an explicitly defined <div id="modal-root"></div> in index.html) should typically exist before the portal attempts to mount to it, or it should be strictly managed via refs/effects to prevent memory leaks.
