0Pricing
Frontend Academy · Lesson

Portals for Modals and Tooltips

Use ReactDOM.createPortal to render children into a different DOM node, enabling modals and tooltips that escape overflow:hidden containers.

Portals for Modals and Tooltips is a free Frontend Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Frontend Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Container Escape Problem

Modals and tooltips need to render at the top of the DOM, escaping overflow:hidden, z-index stacking, and transform-rooted contexts of their parent. Solution: portals.

createPortal API

createPortal(children, container) renders children into container (any DOM node) while keeping them logically children of the React component for events and context.

import { createPortal } from 'react-dom';

function Modal({ children, onClose }) {
  return createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal-content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

Portal Containers

Common targets: document.body, a dedicated #modal-root div in index.html, or a ref'd container created on demand.

<!-- index.html -->
<div id="root"></div>
<div id="modal-root"></div>

// component:
const modalRoot = document.getElementById('modal-root');
return createPortal(<div>...</div>, modalRoot);

Why Not Just Render Inline?

If a parent has overflow: hidden, a child modal gets clipped. If a parent has transform: scale, the modal's fixed positioning becomes relative to the parent. Portals avoid all this.

Events Still Bubble

Despite rendering elsewhere in the DOM, React events bubble through the component tree, not the DOM tree. A click in the portal still bubbles to onClick handlers on ancestor React components.

Closing on Overlay Click

Stop event propagation on the modal content so clicks inside don't close the modal.

function Modal({ onClose, children }) {
  return createPortal(
    <div className="backdrop" onClick={onClose}>
      <div className="content" onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    document.body
  );
}

Closing on Escape Key

Add a keydown listener while the modal is open, removed on unmount.

useEffect(() => {
  const onKey = (e) => { if (e.key === 'Escape') onClose(); };
  window.addEventListener('keydown', onKey);
  return () => window.removeEventListener('keydown', onKey);
}, [onClose]);

Focus Management in Portals

When a modal opens: move focus inside, trap focus until close, then restore focus to the element that opened the modal. Use a library like focus-trap-react to avoid reimplementing this.

Native dialog Element

HTML's <dialog> handles modal positioning, backdrop, focus trap, and Escape close natively. For most modals it's a better starting point than a portal.

function Modal({ open, onClose, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (open) ref.current?.showModal();
    else ref.current?.close();
  }, [open]);
  return (
    <dialog ref={ref} onClose={onClose}>
      {children}
    </dialog>
  );
}

Tooltips and Popovers

Tooltips position relative to an anchor element. Portal lets them escape stacking contexts. Combine with floating-ui for collision detection, flipping, and positioning.

import { useFloating, autoUpdate } from '@floating-ui/react';

function Tooltip({ children, content }) {
  const { refs, floatingStyles } = useFloating({ whileElementsMounted: autoUpdate });
  const [open, setOpen] = useState(false);
  return (
    <>
      <button ref={refs.setReference} onMouseEnter={() => setOpen(true)} onMouseLeave={() => setOpen(false)}>
        {children}
      </button>
      {open && createPortal(
        <div ref={refs.setFloating} style={floatingStyles}>{content}</div>,
        document.body
      )}
    </>
  );
}

SSR Caveat

document doesn't exist on the server. Either render only on the client (check typeof window) or use Next.js's dynamic with ssr:false.

Accessibility: aria-modal

Modal containers should have role="dialog", aria-modal="true", and aria-labelledby pointing to the modal's heading. Or use native <dialog>.

Quick Check

Why use createPortal for modals instead of rendering them inline in the component tree?

Recap: Portals

createPortal(children, container) renders DOM elsewhere while keeping React tree logical. Solves overflow/transform/z-index escape problems. Events still bubble through React tree. Add Escape close + focus management. Native <dialog> handles much of this for modals. Combine with floating-ui for tooltips. Don't forget SSR (typeof window check).

Frequently asked questions

Is the “Portals for Modals and Tooltips” lesson free?

Yes — the full text of “Portals for Modals and Tooltips” is free to read here on the web, and the Frontend Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Frontend Academy course, upgrade to CoddyKit PRO.

What will I learn in “Portals for Modals and Tooltips”?

Use ReactDOM.createPortal to render children into a different DOM node, enabling modals and tooltips that escape overflow:hidden containers. You practise Frontend Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Frontend Academy?

No prior experience is required. Frontend Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Portals for Modals and Tooltips” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Frontend Academy lesson?

Yes. Every Frontend Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Compound Components with Context
  2. Render Props and HOC Patterns
  3. Portals for Modals and Tooltips
  4. Error Boundaries
← Back to Frontend Academy