Portals are a way to render a component in a different place in the DOM tree, outside its parent hierarchy.
Simple analogy: Imagine you are acting in a theater. According to the script you stand on stage (your component in React tree), but your shadow should be projected on the backdrop (portal). Everyone thinks the shadow is yours, but physically it is in a different place.
Why this is needed:
How it works:
1import { createPortal } from 'react-dom';23function Modal({ children }) {4 return createPortal(5 <div className="modal">{children}</div>,6 document.body // Render in <body>, not inside parent7 );8}910function App() {11 return (12 <div style={{ overflow: 'hidden' }}>13 <Modal>I am a modal, but I am not cut off!</Modal>14 </div>15 );16}
Important: Portals preserve React context. Despite the DOM node being in a different place, events (onClick) and context work as if the component stayed in its place in the React tree. Portals do NOT affect performance. They just move the DOM node.
Advanced pattern — portal with escape key handling:
1function Modal({ isOpen, onClose, children }) {2 useEffect(() => {3 const handleEscape = (e) => { if (e.key === "Escape") onClose(); };4 if (isOpen) document.addEventListener('keydown', handleEscape);5 return () => document.removeEventListener('keydown', handleEscape);6 }, [isOpen, onClose]);78 if (!isOpen) return null;9 return createPortal(10 <div className="modal-overlay" onClick={onClose}>11 <div className="modal-content" onClick={e => e.stopPropagation()}>12 {children}13 </div>14 </div>,15 document.body16 );17}