Hey there, Developer! đź‘‹
Have you ever tried to build a Modal or a Tooltip inside a deeply nested component, only to find it getting cut off by a parent with overflow: hidden? Or maybe you found yourself fighting a losing war with z-index? 🤯
I’ve been there. We all have.
Today, we’re going to talk about the solution to this specific headache: React Portals.
Think of it as a teleportation device for your components. 🛸
The Problem: The DOM Trap
By default, when you render a component in React, it gets mounted into the DOM exactly where you put it in the code.
// Parent
<div style={{ overflow: "hidden" }}>
<Modal /> {/* This might get clipped! */}
</div>
If the parent has strict CSS rules (like overflow: hidden or position: relative), your beautiful Modal might be trapped inside, unable to cover the full screen.
We want our Modal to logically belong to the component, but physically exist somewhere else (like attached to the <body>), so it can sit on top of everything.
The Solution: createPortal
React gives us a function specifically for this: createPortal. It’s part of react-dom.
It takes two arguments:
- The child (JSX) you want to render.
- The container (DOM element) where you want to put it.
How to Implement It
First, make sure you have a place in your HTML to land the portal. Usually, in your index.html, you have a <div id="root"></div>. Let’s add another one for modals.
<!-- index.html -->
<body>
<div id="root"></div>
<div id="modal-root"></div> <!-- Target destination -->
</body>
Now, let’s build the Portal component.
import ReactDOM from "react-dom";
const Modal = ({ children, isOpen, onClose }) => {
if (!isOpen) return null;
// We find the element in the actual DOM
const modalRoot = document.getElementById("modal-root");
// Teleport the children to that element!
return ReactDOM.createPortal(
<div className="modal-overlay">
<div className="modal-content">
<button onClick={onClose}>Close</button>
{children}
</div>
</div>,
modalRoot
);
};
export default Modal;
What just happened?
Even though you use <Modal /> deep inside some nested component in your App, the HTML for the modal will actually be injected into <div id="modal-root"></div> at the end of the <body>.
It breaks out of the parent’s styling constraints! 🎉
Event Bubbling: The Cool Part
Here is the mind-blowing part. Even though the Modal is physically somewhere else in the DOM, it logically still behaves like a child of the component that rendered it.
This means Events Bubble Up as you expect.
const App = () => {
const handleClick = () => {
console.log("Click caught in App!");
};
return (
<div onClick={handleClick}>
<Modal isOpen={true}>
<button>Click Me</button>
</Modal>
</div>
);
};
If you click the button inside the Modal (which is physically in modal-root), the onClick handler in App (which is in root) will still fire! React handles this connection for you. Magic. ✨
When to use Portals?
Use them whenever a component needs to “break out” visually:
- Modals / Dialogs: To cover the whole screen.
- Tooltips: So they don’t get clipped by container edges.
- Dropdown Menus: To ensure they float on top of everything else.
- Toast Notifications: To show alerts at the corner of the window.
Closing
React Portals are a powerful tool to have in your utility belt. They solve layout issues that CSS alone makes very difficult.
Remember:
- Import
createPortalfromreact-dom. - Target a DOM node outside your main app root.
- Enjoy z-index peace of mind.
Go fix those broken modals!
Happy Coding! 🚀