Nihar's Dev Corner

How to Improve DOM Structure with React Portal


What is it?

According to the official React documentation,

Portals provide a first-class way to render children into a DOM node that exists outside a DOM hierarchy of the parent component.

Take a look at this example - let's say you have a Calculator component, inside which there is a Buttons component. It would look something like this:

An example of nested components

The root component App along with all its child components will be rendered inside a single DOM node (most likely a div with an id of #root). With React Portal, a child component like Buttons can be rendered outside the div with an id of #root. You can imagine the structure of the DOM to look something like this:

DOM structure of nested components

Why should I know this?

In certain situations, it is necessary to render some JSX or a component outside the root element. The best example of such a situation would be a modal.

A modal would typically cover the entire screen. One would expect this to be reflected in the DOM structure as well. However, that's not what happens in React. If a modal is nested inside a component, that is also how the DOM structure would look like. This can be confusing for people who use screen readers. From an accessibility standpoint, this is an issue.

Resolving this might be a bit easy to do in Vanilla JS and HTML, but harder to do in React. Since React applications have a single mount point, putting an element or component outside this mount point is a challenge. With React Portal, this becomes an easy task.

Although the element or component will be portaled to a different DOM node, it will still function as a regular React element. For our modal this means that it is in a different DOM node than the mount point. But it can still receive props and state and behave like a regular nested React element.

How do I use it?

Let's use the modal example from the section above. Creating a React Portal and mounting it to a different DOM node can be done in 2 steps:

Step 1:

Create another DOM node for mounting the modal:

Creating a separate DOM node

The React application will be mounted to the div with an id of #root. And the modal will be mounted to the div with an id of #modal-root.

Step 2:

Create a Portal wrapper component:

Creating a wrapper component with React Portals

This wrapper component will take a child element and create a Portal for it. The useEffect hook mounts the element and cleans up when the component is unomunted.

Here is how it might look:

Wrapping the Modal in the wrapper component

Even though the Modal component is nested in the app hierarchy, it will look different in the DOM structure:

New DOM structure after using React Portals

In the real DOM structure, you won't see the Modal component directly like this, but a div instead. This is simply a demonstration.

Extra resources

  1. This article has another good use-case for using React Portals.
  2. A dialog component is also a good scenario where React Portals is quite handy.
  3. Do you need a ref from a React Portal? Then this solution for React 16 might help.
  4. Learn how to create a Modal component with React Hooks, Context, and Portals.