React Portals: Solving Z-Index and Stacking Context Issues
When I first learned React, “React Portals” was one of the topics I didn’t really care about. But with time, I came to understand its importance in an application. In this article, I will share about one of the problems I faced.
You might know about Stacking Context
. This represents how our web page's contents are stacked and rendered as per these stacking orders. Stacking contexts are influenced by CSS properties like position
, z-index
, opacity
, transform
, and several others. Whenever we create a new stacking context, a new "layer" is set in the display order, which can sometimes lead to unexpected behavior. You can learn more about this in the documentation. In this case, when you have multiple dropdowns and modals to be rendered within the application but within different stacking contexts, you might face issues like this:
The modal should have been on top. However, as I have a column layout and different stacking contexts, even when I have a high value of z-index for the modal, it appears below the other column. This led me to React Portals.
React Portals
let us render children into a different part of the DOM. So, we can render these modals and dropdowns directly in the body to save ourselves from the abovementioned issues. Let’s look at how we can implement portals in our application to tackle these scenarios.
const ModalBox = ({
children,
handleClose,
open,
size = 'default',
}) => {
// Ensure we're running in the browser environment where `document` is defined
if (typeof document !== 'undefined') {
return ReactDOM.createPortal(
<Modal
modalStatus={open}
closeModalFunction={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
size={size}
>
{children}
</Modal>,
document.body // Render the modal into the body to avoid stacking context issues
);
}
return null;
};
export default ModalBox;
Let me explain what is going on here. I have created a portal and appended the Modal component directly to the document's body. Now, the modal is rendered outside of the parent it’s used in and teleported to the inside of the document's body. So now, whenever the modal is opened, it always displays on top, irrespective of where it’s triggered from.
If you inspect elements, you can see something similar to this:
<body>
<div>
<!-- other elements -->
</div>
<div className="modal">
<!-- modal contents -->
</div>
</body>
Ultimately, this fixes the issue I mentioned above.
React Portals are not confined to modals and dropdowns; they can be utilized wherever an element has to ‘break free’ from its natural DOM hierarchy. This also seems true for tooltips, popovers, notifications, or toasts, as they require the same position and visibility irrespective of their parent component’s rendering context. Though preventing such situations is impossible with regular components, portals would be useful with side panels, drawers, context menus, and overlays since they avoid causing parent elements with overflow: hidden or z-index value conflicts. In the end, Portals allow developers to have full control of rendering and layout, which is useful for any component that has to sit above every other component on the page.
Subscribe to my newsletter
Read articles from Ujjwal Singh Basnet directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by