React Portals provide a way to render children into a different DOM node outside the parent component hierarchy. This is essential for components that need to visually break out of their parent container.
How it works: createPortal(child, container) renders the child React tree into any DOM element you specify, while maintaining the React context and event bubbling through the original React tree.
Complete code example — Modal:
1import { createPortal } from "react-dom";2import { useEffect, useRef } from "react";34function Modal({5 children,6 onClose,7 title,8}: {9 children: React.ReactNode;10 onClose: () => void;11 title: string;12}) {13 const overlayRef = useRef<HTMLDivElement>(null);1415 useEffect(() => {16 const handleEscape = (e: KeyboardEvent) => {17 if (e.key === "Escape") onClose();18 };19 document.addEventListener("keydown", handleEscape);20 document.body.style.overflow = "hidden";21 return () => {22 document.removeEventListener("keydown", handleEscape);23 document.body.style.overflow = "";24 };25 }, [onClose]);2627 return createPortal(28 <div29 ref={overlayRef}30 className="modal-overlay"31 onClick={(e) => {32 if (e.target === overlayRef.current) onClose();33 }}34 >35 <div className="modal-content" onClick={e => e.stopPropagation()}>36 <div className="modal-header">37 <h2>{title}</h2>38 <button onClick={onClose}>X</button>39 </div>40 {children}41 </div>42 </div>,43 document.body44 );45}4647// Usage48function App() {49 const [showModal, setShowModal] = useState(false);50 return (51 <div style={{ overflow: "hidden" }}>52 <button onClick={() => setShowModal(true)}>Open Modal</button>53 {showModal && (54 <Modal onClose={() => setShowModal(false)} title="My Modal">55 <p>Modal content here</p>56 </Modal>57 )}58 </div>59 );60}
When to use portals:
overflow: hidden, z-index stacking, or CSS transformsKey properties:
Example — Toast notifications:
1function Toast({ message, type }: { message: string; type: "success" | "error" }) {2 return createPortal(3 <div className={`toast toast-${type}`} role="alert">4 {message}5 </div>,6 document.getElementById("toast-container")!7 );8}