Portal Architecture
On this page
What a Portal Is
- A portal renders children into a different DOM container.
- React ownership remains the same: events still bubble through React tree.
- Primary use cases: modals, tooltips, toasts, overlays.
Why Portals Matter
- Escape overflow hidden and stacking context constraints.
- Centralize overlay layering rules.
- Enable consistent focus and scroll locking behavior.
Modal Infrastructure Rules
- One root container for overlays, mounted once.
- Centralize z index policy to avoid local z index escalation.
- Lock scroll for body during modal open.
- Trap focus inside the modal and restore focus on close.
Example: Portal Root
function getPortalRoot() {
let el = document.getElementById("portal-root");
if (!el) {
el = document.createElement("div");
el.id = "portal-root";
document.body.appendChild(el);
}
return el;
}
Example: Modal Using createPortal
import { createPortal } from "react-dom";
function Modal({ open, onClose, children }) {
React.useEffect(() => {
if (!open) return;
const onKey = (e) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [open, onClose]);
if (!open) return null;
return createPortal(
<div role="dialog" aria-modal="true">
<div>{children}</div>
<button onClick={onClose}>Close</button>
</div>,
getPortalRoot()
);
}
Event Propagation Notes
- React events bubble through the React tree even when DOM is outside.
- Use stopPropagation carefully, only for overlay click handling.
Failure Modes
- Z index escalation chaos across features.
- Overlay click closes modal unexpectedly due to event bubbling.
- Scroll lock missing causing background scroll under modal.
- Focus not trapped, keyboard navigation leaks to background.
Production Checklist
- Single overlay root exists.
- Z index policy documented and consistent.
- Escape key and outside click behavior defined.
- Scroll lock and focus restoration tested.