I was working on a React project where I needed to show a simple confirmation modal when a user clicked a button.
At first, it was easy. I created a Modal component, added some CSS, and everything looked perfect.
Then the application started getting bigger.
My button was inside a card, the card was inside a dashboard, and the dashboard was inside another container. Some of those parent elements had CSS properties like overflow: hidden and their own z-index values.
That's when my innocent little modal started causing problems. 😅
The modal was supposed to appear on top of the entire page, but instead, parts of it were getting clipped or appearing behind other elements.
So I did what probably every developer does first:
z-index: 9999;
Didn't work.
So I tried:
z-index: 999999;
Still didn't solve the real problem.
At that point, I realized that the problem wasn't really the number of z-index. The modal was simply living inside a DOM structure that was making its job difficult.
That's when I came across something called React Portal.
So, what is React Portal?
In simple terms, a Portal allows us to say:
"Keep this component as part of my React component, but render its HTML somewhere else in the DOM."
Normally:
App
└── Dashboard
└── Card
└── Modal
With a Portal, React can render the Modal somewhere like:
#root
└── App
└── Dashboard
└── Card
#modal-root
└── Modal
The Modal is still part of the same React tree, but its actual DOM element is rendered inside #modal-root.
How do we do it?
First, create another DOM container:
<div id="root"></div>
<div id="modal-root"></div>
Then use createPortal:
import { createPortal } from "react-dom";
createPortal(
<Modal />,
document.getElementById("modal-root")
);
That's basically it.
Why is this useful?
Portals are especially useful for UI that needs to escape its parent's layout, such as:
- Modals
- Dialogs
- Tooltips
- Dropdowns
- Toast notifications
- Full-screen overlays
Instead of fighting with deeply nested CSS and z-index, we can render these elements somewhere more appropriate in the DOM.
The thing that finally made Portals click for me
Before learning about Portals, I assumed:
If a component is inside another component, its HTML must also stay inside that component's DOM.
But that's not necessarily true.
React tree and DOM tree don't always have to be the same.
That's the whole idea behind React Portal.
Once I understood that, Portals stopped feeling like some complicated React feature.
It became a simple solution to a simple problem:
"I want this component to belong here in React, but I want its DOM to live somewhere else."
And sometimes, that's exactly what you need. 🚀
Understanding the #modal-root in this image
Looking at the image, you can see that there are two separate DOM containers:
<body>
├── <div id="root">
│ └── React App
│
└── <div id="modal-root">
└── Modal
At first, this can be confusing.
You might think:
"If
#modal-rootis outside#root, then is the Modal now outside React? Will my state and click events stop working?"
No.
The important thing to understand is that DOM structure and React structure are not necessarily the same.
Normally, without a Portal, you might have:
React Tree + DOM Tree
App
└── Account
└── Modal
But with createPortal():
React Tree
App
└── Account
└── Modal
while the DOM Tree becomes:
DOM Tree
#root
└── App
└── Account
#modal-root
└── Modal
So the Modal's HTML has moved, but the Modal is still connected to the same React component.
That's why things like:
useStatepropscontextonClick- other React logic
still work normally.
For example:
createPortal(
<Modal onClose={() => setShowModal(false)} />,
document.getElementById("modal-root")
);
Even though the Modal is physically rendered inside #modal-root, it can still call setShowModal(false) from the React component that created it.
The main idea
A Portal changes where the component is rendered in the DOM, not where it belongs in the React tree.
So in the image, #modal-root is separate in the DOM, but the Modal is still part of the React application and its React logic continues to work.



Top comments (0)