React Portals let a component render its JSX children into a different DOM element outside its parent container—such as directly inside document.body or a #modal-root node. Portals solve clipping bugs where modals, dropdowns, or tooltips get cut off by a parent element's overflow: hidden or z-index stacking context.
createPortalImport createPortal from 'react-dom' (not 'react-dom/client') and pass two arguments: the JSX to render, and the target DOM node.
import { createPortal } from 'react-dom';
export function ConfirmModal({ isOpen, title, onConfirm, onClose }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-backdrop" role="dialog" aria-modal="true">
<div className="modal-card">
<h2>{title}</h2>
<button type="button" onClick={onConfirm}>Confirm</button>
<button type="button" onClick={onClose}>Cancel</button>
</div>
</div>,
document.body
);
}
flowchart LR
ReactTree["React Tree: Page -> Card -> ConfirmModal"] -->|Context & Events stay in React| AppLogic["Parent onClick / Context"]
ConfirmModal -->|createPortal DOM output| Body["document.body (Top Layer)"]
Even though the modal's DOM elements live inside document.body, a click inside the portal still bubbles up through its React parent components. That means Context providers, state handlers, and parent onClick listeners behave identically to normal children.
document.body During SSR: In server-rendered apps like Next.js, document does not exist on the server. Wait until mount (useEffect + isMounted state) before rendering the portal.document.body does not automatically handle Escape key closing or focus trapping. Add keyboard listeners and return focus to the trigger button when closing.createRoot and createPortal?createRoot starts a brand-new independent React application. createPortal renders a branch of an existing React app into another DOM node while sharing state and Context.
Yes. Because portals live inside your React component tree, useContext reads providers placed above the portal component.
<dialog> instead of a portal?Native HTML <dialog> elements opened with .showModal() render in the browser's top layer automatically, though portals remain useful for custom menus and legacy browser support.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With