A Deep Dive into React Portals: From Theory to Practice
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. 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. To ensure the modal window appears over the main content of the page, we add the necessary CSS styles. 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. 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), 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). Next, we will explore several scenarios by changing the z-index. Which color will be on top in the following example? 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? 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: 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. Next, in the Modal component itself, we import createPor
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.
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.
To ensure the modal window appears over the main content of the page, we add the necessary CSS styles.
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.
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), 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). Next, we will explore several scenarios by changing the z-index
.
Which color will be on top in the following example?
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?
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:
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.
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
.
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.
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.
✔️ 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!
What's Your Reaction?