DEV Community

Cover image for Rendering Tooltips with React Portals (Without Breaking Layout)
HexShift
HexShift

Posted on

Rendering Tooltips with React Portals (Without Breaking Layout)

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)

Collapse
 
adam_dream_eng profile image
Adam Smith

Waiting for the day we can use pure css with popover and anchor positioning

Collapse
 
nevodavid profile image
Nevo David

super clean way to dodge all those layout headaches, honestly love it

Collapse
 
hexshift profile image
HexShift

Thanks very much!

Collapse
 
rohitkhokhar profile image
Rohit Khokhar

Amazing! Thanks for sharing 🙌

Collapse
 
hexshift profile image
HexShift

Thank Rohit, check out the PDF file if you want to get a firm grip on React Portals.

Collapse
 
barbot profile image
William Zhang

Nice! Thanks