While React Portals are commonly used for modals, they’re just as valuable for lightweight UI elements like tooltips and popovers. Tooltips often need to escape container boundaries (like overflow: hidden
) and stack correctly above all other content. That’s exactly where React Portals shine.
In this article, we’ll use a portal to render a tooltip outside the parent component’s DOM tree — avoiding layout bugs and z-index conflicts.
Step 1: Set Up a Portal Root
Just like with modals, create a separate DOM node for your tooltip content in public/index.html
:
<body>
<div id="root"></div>
<div id="tooltip-root"></div>
</body>
This is where the tooltip will be mounted via a portal.
Step 2: Create the Tooltip Component
Let’s build a tooltip that renders via ReactDOM.createPortal
and positions itself near a target element using getBoundingClientRect()
.
import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
const tooltipRoot = document.getElementById("tooltip-root");
export default function Tooltip({ targetRef, text, visible }) {
const [position, setPosition] = useState({ top: 0, left: 0 });
const tooltipRef = useRef(null);
useEffect(() => {
if (visible && targetRef.current) {
const rect = targetRef.current.getBoundingClientRect();
setPosition({
top: rect.bottom + window.scrollY + 8,
left: rect.left + window.scrollX + rect.width / 2,
});
}
}, [visible, targetRef]);
if (!visible) return null;
return ReactDOM.createPortal(
<div
ref={tooltipRef}
className="tooltip"
style={{ top: position.top, left: position.left }}
>
{text}
</div>,
tooltipRoot
);
}
Step 3: Style the Tooltip
Add basic styles for the floating tooltip:
.tooltip {
position: absolute;
transform: translateX(-50%);
background: #222;
color: #fff;
padding: 6px 10px;
border-radius: 4px;
font-size: 0.875rem;
white-space: nowrap;
pointer-events: none;
z-index: 1000;
}
This ensures the tooltip is small, centered above the target, and doesn't block pointer events.
Step 4: Use the Tooltip
Now let’s use it in a component:
import React, { useRef, useState } from "react";
import Tooltip from "./Tooltip";
function TooltipExample() {
const btnRef = useRef();
const [hovered, setHovered] = useState(false);
return (
<div style={{ padding: "100px" }}>
<button
ref={btnRef}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
Hover me
</button>
<Tooltip
targetRef={btnRef}
text="This is a tooltip rendered via portal"
visible={hovered}
/>
</div>
);
}
When the button is hovered, the tooltip appears — positioned outside the normal DOM flow, thanks to the portal.
✅ Why Use Portals for Tooltips:
- 🚫 Avoid clipping inside scrollable or overflow-hidden containers
- 🧼 Keeps z-index and stacking predictable
- 📦 Lightweight and easily reusable
Summary
React Portals aren’t just for full-screen modals — they’re perfect for tooltips, dropdowns, and overlays that need to “break out” of the DOM layout. By positioning your tooltip with getBoundingClientRect()
and rendering it in a portal, you get precise control over placement and styling — without DOM headaches.
For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:
Using React Portals Like a Pro.
If this was helpful, you can also support me here: Buy Me a Coffee ☕
Top comments (6)
Waiting for the day we can use pure css with popover and anchor positioning
super clean way to dodge all those layout headaches, honestly love it
Thanks very much!
Amazing! Thanks for sharing 🙌
Thank Rohit, check out the PDF file if you want to get a firm grip on React Portals.
Nice! Thanks