A Deep Dive into React Portals: From Theory to Practice
Tags: webdev, react, beginners, programming
Published: 2024-03-24 15:08:20

I remember when I first attempted to create a modal window, I turned to YouTube looking for detailed explanations or tutorials. I was aware of _React Portal's_ existence but had no experience using it. To my surprise, I found that most recommendations were about creating a simple modal component without utilizing the portal. This approach can lead to unexpected errors since such a component might be reused across different parts of an application. But let's start from the beginning.

In this article, I will explain in detail the advantages of _React Portal_, the problems it can solve, and why using it is the perfect solution for creating modal windows, tooltips, and other elements that need to appear on top of the page's other content.

## ✔️ Modal
Firstly, let me clarify what I mean when I talk about a standard modal component without a portal.

It's a standard component that accepts `children` as props and renders content passed from the parent component.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuwsh1wel2u5eve8463g.png)


The parent component manages the state of the modal window using the `useState` hook. This state changes when buttons are clicked in both the parent component and the modal itself.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3mr7hlglah0s4oy2xn0e.png)

To ensure the modal window appears over the main content of the page, we add the necessary CSS styles.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdu15r8dgve47ct2wrwb.png)

This is a very simple example of implementing a modal window, and the logic behind its creation doesn’t change much when using _React Portal_. However, there's an important detail - the **stacking context**, which we'll discuss next.

## ✔️ Stacking context

Almost every front-end developer has encountered a scenario where, despite a high `z-index`, an element doesn't appear on top of all others. The cause of this situation is the stacking context. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inaklryrefo43itnl4aj.jpeg)

According to documentation, the stacking context is a concept of three-dimensional arrangement of HTML elements along the Z-axis relative to the viewer. By default, all elements on the page are placed in the order they appear in the HTML. However, under certain conditions, such as absolute positioning with a `z-index` different from `auto`, `opacity` less than `1`, among others (more details [here](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context)), a new stacking context can be formed.

Consider an example where several absolutely positioned blocks of the same size but different colors are created ([codepen](https://codepen.io/Pavlo-Reutskyi/pen/wvZdzbo)). Next, we will explore several scenarios by changing the `z-index`.

Which color will be on top in the following example?

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/78j2a0ghqywcwqfrr1gk.png)

The correct answer is **yellow**. This is because at the `body` level, there exists a basic stacking context that determines the display order of the `green` and `blue` elements. Since `blue` has a higher `z-index`, it and all its child elements will be placed above all others at the same level. 

Let's consider another example with a slight change in conditions. Which element will now be higher than the others?

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mkk5hsfzwk3lk0js8viu.png)

The correct answer is **orange**. The `blue` element, within its stacking context, "defeats" neighboring elements while simultaneously creating a new stacking context. Its child elements (`yellow` and `orange`) will compete with each other within this context. The `z-index` of the parent element, though higher, is not considered.

This can be likened to a wardrobe with shelves stacked on top of each other, which can be rearranged at will. Each shelf contains clothes arranged one on top of the other. The bottom shelf may contain many items, but they will always be lower than even a single item on the top shelf. The shelves and the clothes on each shelf represent stacking contexts.

And here's the final example:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7l2uw7ho970qu5lsvcpp.png)

You might have guessed that the behavior of the modal window will not be as expected, as the `blue` element, with its higher `z-index`, will not allow it to unfold over all other elements on the page.

In this simple example, the problem is evident. However, when dealing with a large page composed of many components, such a scenario can easily go unnoticed. Moreover, the `Modal` component might work perfectly on some pages and encounter problems on others. Often, such issues are challenging to diagnose.

But there is a solution – **React Portal**!

## ✔️ React Portal

Portals offer a unique capability in React: they allow us to render child elements into different parts of the DOM that exist outside the parent component's DOM hierarchy, thus making them independent of the stacking context. This is achieved using the `createPortal()` method, which can take up to three arguments:

- `children` - can be anything from JSX, components, to even simple strings or numbers.

- `domNode` - this is the DOM node where the portal will be placed. Typically, it's an element created outside the root element to which the React app is directly attached.

- `key` - an optional parameter that can be a number or a string, serving as a unique identifier for the portal.

Let's modify our `Modal` component to use React Portal.

First, we add an element with the identifier `portal`, where our portal will be added. The identifier can be any value.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sit082w3hs7eoc6cro6d.png)

Next, in the `Modal` component itself, we import `createPortal` from the `react-dom` library. Instead of returning JSX as we did before, we use `createPortal`. The first parameter is what we want to add, and the second parameter is where we want to add it. In our case, it's the element with the identifier `portal`, adjacent to `root`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxg85yht503qd6evxfgw.png)

And just like that, it's done! We can now use the `Modal` component as before, but with the confidence that this component and the `children` passed into it will always be on top of the page because it is located outside its bounds.

In general, we can highlight two key advantages and features of React Portal.

🔹 As we've already discovered, the first advantage is that the portal allows us to bypass the limitations imposed by the parent element, especially through the stacking context or, for example, when using `overflow: hidden`.

🔹 The second, and perhaps most interesting feature, is that although the portal changes the physical location of the element in the DOM, it does not change its behavior relative to the React hierarchy. This means our `Modal` component continues to behave as if it is a regular child element of the component rendering it. This includes the ability to use Context and the rules of event propagation.

These features, presumably, explain the name. The component, using a portal, seemingly teleports to the required place in the React app but behaves as if it has always been there.

## ✔️ React Portal in Next.js and Server Components

When using portals in **Next.js**, a few considerations need to be taken into account.

Since a portal manipulates the DOM directly, it can only be a **client component**, which requires the `"use client"` directive. Additionally, before adding a portal, you need to ensure the DOM has been fully loaded. This can be achieved by using state and the `useEffect` hook, which runs at the end, after the component has been rendered.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vg1ieexe3tiw455uue3.png)

Although the portal will always be a client component, the `children` passed into it can still be **server component**, but only if the parent component is also server-side.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xicbbarvwkeo0rujg2f2.png)

## ✔️ Summary

**React Portal** is a cool and powerful tool. It may not be necessary for every modal implementation, especially in smaller projects, but there are many scenarios where it can significantly help and simplify a developer's life.

I hope this article has been helpful and demonstrated when and how to use React Portal effectively. It not only solves specific challenges efficiently but also opens up new possibilities for optimization and enhancing the user experience in your applications.

Thank you for your time and wish you success on your exciting journey of web development with React!