π React Portals: The Magic Teleportation Door
The Story of the Trapped Popup
Imagine you have a toy box. Inside the toy box, you keep all your favorite toys. Now, what if you wanted to show your balloon to someone outside your room, but the balloon is stuck inside the toy box with a tiny lid?
Thatβs exactly the problem React components face!
In React, every component lives inside its parent. Itβs like toys in a toy box. But sometimes, a componentβlike a popup or a tooltipβneeds to appear above everything else on the screen. If it stays inside its parent, it might get hidden or cut off!
This is where Portals come in. Think of a Portal as a magic door that teleports your component to a different place in the pageβwhile still keeping it connected to its React family.
π― Portal Fundamentals
What is a Portal?
A Portal is like a secret tunnel that lets a React component render somewhere else in the HTML documentβnot where it normally belongs.
Normal Rendering:
βββββββββββββββββββββββ
β Parent Component β
β ββββββββββββββββββ β
β β Child (Modal) β β β Stuck inside!
β ββββββββββββββββββ β
βββββββββββββββββββββββ
With Portal:
βββββββββββββββββββββββ
β Parent Component β
β (Portal magic!) β
βββββββββββββββββββββββ
βββββββββββββββββββββββ
β Modal (teleported!) β β Now at the top!
βββββββββββββββββββββββ
Why Do We Need Portals?
Problem: CSS overflow: hidden or z-index issues can trap your popups and modals inside their parent containers.
Solution: Portals let your component escape to a different part of the DOM tree, usually directly inside <body>.
Key Concept
Even though a Portal renders somewhere else in the DOM, it still behaves like a normal React child:
- β It can access the same context
- β Events bubble up through the React tree
- β State and props work normally
π οΈ The createPortal API
How to Create a Portal
React gives you a special function called createPortal. Itβs like saying: βTake this component and put it over there!β
import { createPortal } from 'react-dom';
function MyModal({ children }) {
return createPortal(
children,
document.body
);
}
The Recipe (Two Ingredients)
graph TD A["createPortal"] --> B["What to render"] A --> C["Where to render it"] B --> D["Your component/JSX"] C --> E["DOM element like document.body"]
Syntax Breakdown
createPortal(child, container)
| Ingredient | What It Is | Example |
|---|---|---|
child |
The React element to teleport | <div>Hello!</div> |
container |
The DOM node destination | document.body |
Complete Example: A Simple Modal
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay">
<div className="modal-box">
{children}
<button onClick={onClose}>
Close
</button>
</div>
</div>,
document.body
);
}
Usage:
function App() {
const [showModal, setShowModal] =
useState(false);
return (
<div>
<button onClick={() =>
setShowModal(true)}>
Open Modal
</button>
<Modal
isOpen={showModal}
onClose={() =>
setShowModal(false)}>
<h2>I'm teleported!</h2>
</Modal>
</div>
);
}
Creating a Custom Container
Sometimes you want your portal to go to a specific div, not the body:
// In your HTML:
// <div id="modal-root"></div>
function Modal({ children }) {
const container = document.getElementById(
'modal-root'
);
return createPortal(
children,
container
);
}
πͺ Portal Use Cases
1. Modals & Dialogs
The most common use! Modals need to appear above everything.
function ConfirmDialog({ message, onYes }) {
return createPortal(
<div className="dialog">
<p>{message}</p>
<button onClick={onYes}>Yes</button>
</div>,
document.body
);
}
2. Tooltips
Tooltips must float above all content without being clipped.
function Tooltip({ text, position }) {
return createPortal(
<div
className="tooltip"
style={{
top: position.y,
left: position.x
}}>
{text}
</div>,
document.body
);
}
3. Dropdown Menus
When dropdowns are inside containers with overflow: hidden, they get cut off. Portals save the day!
function Dropdown({ items, anchorRect }) {
return createPortal(
<ul
className="dropdown"
style={{
top: anchorRect.bottom,
left: anchorRect.left
}}>
{items.map(item => (
<li key={item.id}>
{item.label}
</li>
))}
</ul>,
document.body
);
}
4. Notification Toasts
Notifications that pop up in a corner of the screen.
function Toast({ message }) {
return createPortal(
<div className="toast">
{message}
</div>,
document.getElementById('toast-root')
);
}
Use Cases Summary
graph TD A["Portal Use Cases"] --> B["πͺ Modals"] A --> C["π¬ Tooltips"] A --> D["π Dropdowns"] A --> E["π Notifications"] A --> F["π¨ Floating UI"] B --> G["Above all content"] C --> G D --> G E --> G F --> G
π The Magic in Action
Hereβs the beautiful thing: even though your Modal lives in document.body, it still acts like itβs inside your component:
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<Modal isOpen={true}>
{/* This button still works! */}
<button onClick={() =>
setCount(c => c + 1)}>
Add from Modal
</button>
</Modal>
</div>
);
}
Event bubbling works normally! Clicks inside the portal bubble up through the React component tree, not the DOM tree.
π Quick Recap
| Concept | Remember This |
|---|---|
| Portal | Magic door to render elsewhere |
| createPortal | The spell: createPortal(what, where) |
| Use Cases | Modals, tooltips, dropdowns, toasts |
| Superpower | Escapes CSS traps while keeping React powers |
π‘ Pro Tips
-
Always have a container ready β Create a
<div id="modal-root">in your HTML. -
Clean up! β If you create DOM nodes dynamically, remove them when the component unmounts.
-
Accessibility matters β Use proper ARIA attributes for modals and dialogs.
-
Z-index is still important β Just because you portal doesnβt mean you skip z-index management!
Congratulations! π You now understand React Portals. You can teleport components anywhere on the page while keeping all the React magic intact. Go build those beautiful modals and tooltips!
