DEV Community

Cover image for React Portals: How They Work and When to Use Them for Modals and Tooltips
Srikar Phani Kumar Marti
Srikar Phani Kumar Marti

Posted on Originally published at blog.mspk.me

React Portals: How They Work and When to Use Them for Modals and Tooltips

You’ve probably built a modal or tooltip in React and hit a weird snag: the overlay appears visually outside your usual component tree, but events don’t behave as you expect. Maybe clicks inside the modal don’t bubble the way you thought, or keyboard focus seems lost when you open it.

That’s React portals messing with your head , in a good way. They let you render children into a DOM node outside your React root, which is great for modals, tooltips, dropdowns, and other overlays. But under the hood, portals change the event propagation and focus flow in subtle ways.

I ran into this recently while debugging a modal that wouldn’t close on outside clicks. The culprit? A misunderstanding of how portals handle events and focus in the DOM.

Let me walk you through how React portals work, what happens to events and focus, and some practical debugging tips when using portals for modals and tooltips.

What Exactly Is a React Portal?

React portals are an escape hatch. Normally, React renders your component tree inside a single root DOM node , often <div id="root">. But what if you want a component somewhere else in the DOM, like a modal appended to document.body?

Portals let you do that:

ReactDOM.createPortal(children, container)
Enter fullscreen mode Exit fullscreen mode

This tells React: render children into DOM container, outside the main React DOM tree.

Why?

  • Modals and tooltips often need to break out of parent containers that may have overflow: hidden or z-index stacking contexts.
  • They need to sit at a higher DOM level for proper layering and positioning.

So portals let you keep React’s declarative component model while placing DOM nodes where they need to be.

What Happens Under the Hood?

React internally keeps track of the portal’s target container. It renders the portal’s children into that container as real DOM nodes.

But React’s component tree and the DOM tree then diverge. Your React tree says "Modal is a child of App," but the DOM says "Modal is a child of document.body."

This divergence is the core of what trips people up.

Event Propagation Isn’t Broken , But It’s Different

Here’s the key: React uses a single event delegation system at the root container. Events bubble through the DOM, then React maps them back to components.

With portals, the DOM tree is split. The portal’s DOM nodes live somewhere else, outside the root container where React listens for events.

So how does React handle events from portal nodes?

React attaches event listeners at the root of each React render tree , and for portals, it attaches them at the portal’s container node too.

This means events inside a portal bubble through the portal container, then propagate to the React root container.

In practice, this usually works seamlessly, but there are some event propagation edge cases:

  • Events that rely on the DOM tree hierarchy (like native event bubbling) behave normally because the portal nodes are real DOM children of the portal container.
  • But React’s synthetic event system treats portal containers as roots for event delegation, so some event listeners might fire in different orders.

This explains why outside click detection on portals sometimes fails if you’re relying on event bubbling inside the React tree without accounting for the portal’s container boundaries.

Focus Management: The Invisible Trap

Modals and tooltips often trap focus for accessibility: when the modal opens, keyboard focus should move inside it, and tabbing should cycle within the modal.

Portals complicate this because the modal’s DOM nodes are outside the React root and its focus context.

Browsers don’t care about React internals , they only see DOM nodes. So focus will move normally, but your app’s logic to restore focus or detect focused elements needs to account for the portal container.

For example, if you try to detect clicks outside a modal by checking if the event target is inside your React tree, it won’t work , because the modal lives in a separate DOM subtree.

Also, keyboard navigation and focus outlines might behave unexpectedly if CSS styles or focus management code assume the modal is nested inside the React root.

Practical Debugging Tips for Portal-Based Modals and Tooltips

1. Use ref on the Portal Container

Keep a ref to the portal container DOM node (document.body or a div you created). When handling clicks or focus events, check if the event target is inside that container using container.contains(event.target).

This is more reliable than React tree checks.

2. Attach Event Listeners on the Portal Container

For outside click detection, attach mouse event handlers on the portal container or even document.

React’s synthetic events may not catch all events correctly when portals are involved, so sometimes native event listeners are more reliable.

3. Manage Focus Explicitly

When opening a modal:

  • Focus the first focusable element inside the portal.
  • Trap tab focus inside the portal container.
  • Return focus to the opener element when closing.

Use libraries like focus-trap-react or build your own with careful DOM traversal.

4. Watch for CSS Stacking Context Issues

Since portals render outside your main tree, your modal or tooltip might be affected by z-index, position, or overflow styles on ancestor nodes.

Debug with DevTools by inspecting the portal container and ancestors to ensure it’s visible and on top.

5. Beware of Event Ordering Differences

If you have both portal and non-portal components listening for the same events, event firing order might surprise you.

Test carefully and consider using native event listeners in tricky cases.

When Should You Reach for Portals?

Use portals when you need UI elements that:

  • Break out of clipping or stacking of parent containers
  • Need to be siblings of your root app node for layering
  • Require focus management separate from the main React tree

Modals, tooltips, dropdown menus, fullscreen overlays, and context menus are classic portal use cases.

If your popup content can stay within parent containers without clipping or z-index issues, portals might be overkill and add complexity.

A Real Debugging Story

I once built a tooltip that disappeared as soon as I clicked inside it , even though I had an outside click handler to close it only if the click was truly outside.

The problem was the click event inside the portal was bubbling to the portal container, but my handler was checking if the click target was inside the React component tree , which it wasn’t.

Solution: I switched to checking if the click target was inside the portal container DOM node instead. Suddenly, clicks inside the tooltip didn’t trigger the outside click handler.

That tiny change saved me hours of head-scratching.

Wrapping Up

React portals let you render components outside your main React root for UI elements like modals and tooltips. This is powerful but comes with quirks:

  • Event propagation crosses DOM boundaries differently than standard React tree events.
  • Focus management requires explicit handling because the portal’s DOM nodes are outside your main tree.
  • Debugging portal issues means thinking in terms of DOM containment and event flow outside React’s usual model.

Next time your modal misbehaves or your tooltip’s keyboard navigation feels off, remember: portals are working exactly as designed , but you might need to adapt your event and focus logic to their outside-the-tree reality.


Helpful learning resources

Top comments (0)