React Portals allow you to render components outside the main DOM hierarchy — essential for UI elements like modals, tooltips, and dropdowns that must break out of parent boundaries. In this tutorial, you'll learn how to build a powerful modal system using portals and hooks in a clean, scalable way.
Step 1: Create a Portal Target
Add a dedicated <div>
in your HTML (typically inside public/index.html
) for rendering portal content.
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>
Step 2: Build a Modal Component Using createPortal
Use ReactDOM.createPortal
to render the modal into the #modal-root
.
import { useEffect } from "react";
import ReactDOM from "react-dom";
function Modal({ children, onClose }) {
useEffect(() => {
const handleEsc = (e) => e.key === "Escape" && onClose();
window.addEventListener("keydown", handleEsc);
return () => window.removeEventListener("keydown", handleEsc);
}, [onClose]);
return ReactDOM.createPortal(
<div className="overlay" onClick={onClose}>
<div className="modal" onClick={(e) => e.stopPropagation()>
{children}
</div>
</div>,
document.getElementById("modal-root")
);
}
Step 3: Use the Modal in Your App
You can now drop in modals anywhere without worrying about z-index, CSS overflow, or component nesting limitations.
import { useState } from "react";
function App() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Open Modal</button>
{open && (
<Modal onClose={() => setOpen(false)}>
<h2>This is a modal!</h2>
<p>Click outside or press Escape to close.</p>
</Modal>
)}
</>
);
}
✅ Pros
- Avoids z-index conflicts and CSS containment issues.
- Great for modals, popovers, tooltips, or context menus.
- Scales cleanly across apps without tight coupling.
⚠️ Cons
- Requires manual DOM target setup.
- Can complicate testing and debugging.
- Accessibility (e.g., focus trap) needs extra handling.
Wrap-Up
React Portals are a must-have pattern for any advanced frontend project. With minimal boilerplate, you can render interactive components that feel truly native to modern UIs—without breaking layout or structure.
Ready to dive deeper now? Unlock the full potential of React Portals with my detailed 24-page PDF guide, available for just $10! You'll get step-by-step instructions on mastering everything from the basics to advanced portal techniques. Learn to create dynamic modals, optimize performance, handle nested portals, and ensure accessibility—all through real-world examples and best practices. This PDF is a must-have for any React developer looking to elevate their UI development skills. Don’t miss out on this essential resource—get your copy now for just $10 and start building more powerful and scalable React applications today!
If you enjoyed this, you can also support future dev-focused tutorials here: Buy Me a Coffee ☕
Top comments (7)
dev.to/jackfd120/build-a-reusable-...
Check out this doc, I believe you'll more helpful by this article while using Next.js and React.
You can use the
AbortControlle
for removing all listeners like:This way allows not to remove each listener separately but all at once
Good point. This should also work...
useEffect(() => {
const controller = new AbortController();
const { signal } = controller;
const handleEsc = (e) => {
if (e.key === "Escape") {
onClose();
}
};
window.addEventListener("keydown", handleEsc, { signal });
return () => controller.abort();
}, []);
Learn to build advanced modals, tooltips, and overlays with React Portals. Master techniques to handle complex UI components seamlessly. For more, visit alvlaskurin.fi/ and boost your React skills today!
That's very interesting! I always used z-index with some display type like "absolute".. and indeed, in some cases I had to "investigate" where CSS conflicts come from...
So the idea is to have a completely separated parent, for all hovering? if so, must it be a completely top level up to portal?
I wonder if the same pattern would work the same with high enough parent but not necessarily a top portal...
Yeah, the main benefit of using a Portal is that it lets you escape the normal DOM hierarchy, which helps a ton with tricky z-index and overflow: hidden issues (especially in modals, dropdowns, and tooltips).
You can sometimes get away with just having a high enough parent container, but it’s more fragile, depends heavily on how the rest of your app is styled. A Portal guarantees you're rendering into a separate top-level DOM node (usually right under
), so you don’t have to fight with nesting styles or layout containment.So TL;DR: Portals aren’t always strictly necessary, but they’re a great way to make your UI components more reliable and portable, especially as your app grows.
Hope that helps! Let me know if you experiment with a hybrid approach. I’d be curious what works for you.
Learn how to master React Portals and build advanced modals, tooltips, and overlays. Perfect for developers looking to enhance UI functionality. Plus, discover how to download FF beta testing for cutting-edge features and improvements in your projects!