Display on hover


The previous lesson explained how to create a Tooltip component written in html that locates itself at the right position, on top of the SVG area.

Now it's time to make it appear only on hover. The implementation looks a lot like what we've done for hover effects, so you'll likely find it straightforward!

Members only
5 minutes read

What we're building

The tooltip from the previous lesson was always visible, but the heavy lifting was already done: component, styling, positioning. Now we just need to reveal it only when the user is hovering a marker.

The trick: an internal state that flips on hover. Here's the full result before we break it down:

import { useState } from "react";
import { Tooltip } from "./Tooltip";
import "./styles.css";

const width = 500;
const height = 300;

export default function App() {
  const [interactionData, setInteractionData] = useState(null);

  return (
    <div style={{ position: "relative" }}>
      {/* SVG layer */}
      <svg width={width} height={height}>
        <circle
          cx={width / 2}
          cy={height / 2}
          r={30}
          fill="#4e79a7"
          onMouseEnter={() =>
            setInteractionData({
              xPos: width / 2,
              yPos: height / 2,
              name: "Hello tooltip",
              xValue: width / 2,
              yValue: height / 2,
              color: "#4e79a7",
            })
          }
          onMouseLeave={() => setInteractionData(null)}
        />
      </svg>

      {/* Tooltip layer */}
      <div
        style={{
          position: "absolute",
          width,
          height,
          top: 0,
          left: 0,
          pointerEvents: "none",
        }}
      >
        <Tooltip interactionData={interactionData} />
      </div>
    </div>
  );
}

Hover over the blue circle. Move away to dismiss the tooltip.

How it works: 3 small moves

The whole thing boils down to a single state that answers one question: which marker is the user currently hovering?

Fill it on onMouseEnter, clear it on onMouseLeave, and let the Tooltip component render from it.

1️⃣ A state for the active marker

We hold the currently hovered marker in a single piece of state. When nothing is hovered, the state is null.

Otherwise, it's an interactionData object with everything the tooltip needs to render. Shaped exactly like the prop the Tooltip component already expects, which is not a coincidence.

import { useState } from "react";

const [interactionData, setInteractionData] = useState(null);

2️⃣ Fill the state on hover, clear it on leave

Attach onMouseEnter and onMouseLeave directly to each SVG marker. On enter, we push the marker's data into state. On leave, we reset to null.

<circle
  cx={250}
  cy={150}
  r={30}
  fill="#4e79a7"
  onMouseEnter={() =>
    setInteractionData({
      xPos: 250,
      yPos: 150,
      name: "hello",
    })
  }
  onMouseLeave={() => setInteractionData(null)}
/>
Without onMouseLeave, the tooltip would linger forever once the cursor moves off the circle. The exit handler is as essential as the entry one.

3️⃣ Render the tooltip from state

Our Tooltip component already returns null when its prop is null (remember the early-return from the previous lesson). So we just pass the state down and let the component do the right thing:

<div
  style={{
    position: "absolute",
    width,
    height,
    top: 0,
    left: 0,
    pointerEvents: "none",
  }}
>
  <Tooltip interactionData={interactionData} />
</div>

⚠️ Don't forget pointerEvents: "none" on the overlay. If the overlay catches the mouse, the onMouseEnter on the SVG shapes underneath will never fire, and the tooltip will never appear in the first place.

Oh no! 😱

It seems like you haven't enrolled in the course yet!

Join many other students today and learn how to create bespoke, interactive graphs with d3.js and React!

Enrollment is currently closed. Join the waitlist to be notified when doors reopen:

Or Login