DEV Community

Cover image for How to make an extremely reusable Tooltip component with React — and nothing else
Vitor Paladini
Vitor Paladini

Posted on • Edited on • Originally published at paladini.dev

How to make an extremely reusable Tooltip component with React — and nothing else

EDIT: Ok, after publishing I realized that "nothing else" is an exaggeration. There is quite a bit of CSS too. But no other JS dependency 🙈


Tooltips are a fantastic way of adding context to a piece of UI.

Recently I had to use lots of them at work. And I'm a rather lazy person, so to make it easier I created a <Tooltip /> component that I could use to add tooltips to pretty much anything.

It is a no-sweat component, with no dependency other than React itself.

Be aware that most of the patterns applied here are not exclusive to React so you may learn a few new things even React is not your cup of tea. 🙂

Here's a demo before we move into the details:

How to make it

What makes this component so good is that it leverages good old CSS sorcery with the flexibility of React children prop. We only need two files to make it work: Tooltip.css and Tooltip.js.

Let's talk about the CSS of it first.

Tooltip.css

There's a handful of techniques at play here:

  1. Custom properties (CSS vars) that control color, spacing and arrow size
  2. CSS border triangles and before pseudo-elements to make the arrows
  3. Some smart absolute positioning and wrapping to put everything in the right place

Have a look at the Tooltip.css file.

You can see that half of it is styling to make the tooltip appear in different directions. A CSS preprocessor could make this code leaner but remember, we are keeping it simple.

The biggest takeaway of Tooltip.css is understanding that by wrapping a React component with <Tooltip> we are also wrapping it with an element styled by the Tooltip-Wrapper class.

That CSS class anchors the positioning of the tooltips with position: relative. That way we can use position: absolute in each tooltip with its top, right, bottom, and left values relative to the element we are wrapping.

Now that we understand that Tooltip.css handles how the tooltip looks and where it goes, let's talk about its .js counterpart.

Tooltip.js

Tooltip.js does four important things:

  1. It takes everything inside a <Tooltip> component and moves it inside a div with Tooltip-Wrapper class by using props.children
  2. It controls what content will be inside the tooltip bubble with props.content
  3. It controls where the bubble will appear using the value passed to props.direction as a class.
  4. It controls when it shows by listening to onMouseEnter and onMouseLeave events

Here, have a snoop at Tooltip.js:

Can you see how it works together with Tooltip.css?

The biggest takeaway of this file is that it has the minimal necessary logic to make CSS shine. All the work it does is moving the props you passed to <Tooltip> into the right places.

So at the end of the day (or at the end of the reconciliation 😄), all that Tooltip.js does is transforming this:

<Tooltip content="Hello, I'm a tooltip" direction="right">
  <button>I'm a button</button>
</Tooltip>
Enter fullscreen mode Exit fullscreen mode

Into this:

<div
  className="Tooltip-Wrapper"
  onMouseEnter={showTip}
  onMouseLeave={hideTip}
>
  <button>I'm a button</button>
  {active && (
    <div className={`Tooltip-Tip right`}>
      Hello, I'm a tooltip
    </div>
  )}
</div>
Enter fullscreen mode Exit fullscreen mode

How to use it

After learning how it works, the "how to use it" should be pretty simple to grasp.

All you need to do is import the Tooltip component and use it as a wrapper. Make it go above anything you want to show a tooltip on hover.

It takes three props:

  1. content, which will be what's inside the tooltip
    • Required, It can be anything JSX, text, images, other components, it's up to you
  2. direction, which controls where the tooltip will show
    • Optional, accepts top, right, bottom, and left. Defaults to top
  3. delay, how much time, in milliseconds, it takes for the tooltip to show.
    • Optional, defaults to 400ms

Add a simple wrap with a some of these props and bam now every hover on anything that is inside <Tooltip> will show a small balloon of content.

Here's how I did it in the demo:

Pretty cool, right?

What I love the most about modern web development is how components make stuff easier to implement after some initial setup.

Doing the same thing during jQuery times would take a lot of repetition, duplication, and much more elbow grease.

And as a final reflection, I'm sure that some things in front-end look crazy complex now but these kinds of techniques make me feel that we are moving in the right direction.


And that's it, thanks for reading. I hope this article is useful on your front-end journey!

As always, comments and feedback are super welcome, so what would you change or improve in this implementation?

Cover photo by Drew Beamer on Unsplash


Hey, let's connect 👋

Follow me on Twitter and let me know you liked this article!

And if you really liked it, make sure to share it with your friends, that'll help me a lot 😄

Top comments (11)

Collapse
 
jamesncox profile image
James Cox

I love when someone breaks something down you might see from a React component library like Material UI into their own reusable React components. I appreciate the time and effort you took to recreate something that most of us might take for granted. Great work!!

Collapse
 
vtrpldn profile image
Vitor Paladini

Hey, James. Thank you! I think that there is a lot to learn from doing so and it makes me super happy to know that I'm not the only one learning with it 😄

Collapse
 
jamesncox profile image
James Cox

Absolutely! There is always value in "looking under the hood" and recreating how something works. It reminds me of a recent DEV post that recreated common built-in JavaScript functions like map. You got me thinking about what component I could try to recreate. I sort of did that with my side navigation bar in my newest portfolio site, but it's not exactly as reusable as your tooltip!

Collapse
 
vtrpldn profile image
Vitor Paladini

Your head is the right place but I'd say my solution scratches a different itch.

title attr is great until you need to style or add custom behavior to your tooltips. If it was the absolute solution 100% of the time we wouldn't have decade-old tooltip libraries. 🙂

Collapse
 
gsarig profile image
Giorgos Sarigiannidis

Nice job!

A few thoughts:

  • Maybe it would make sense to add an option to trigger the tooltip on click instead of hover.
  • Hardcoding the direction might be problematic on some occasions. For example, on the desktop the tooltip might be better on the right, but on a smaller screen and depending on the layout, it might need a different alignment. So, a direction "auto" could be added, which would calculate the proper position depending on the element's position on the screen.
  • Since the tooltip can accept HTML, links, etc, it would be nice if it would stay open when moving the cursor above it. This could be handled with just CSS I think.
Collapse
 
vtrpldn profile image
Vitor Paladini • Edited

Hey, Giorgio. Thanks! You have excellent points.

Maybe it would make sense to add an option to trigger the tooltip on click instead of hover.

Agreed. IMO, tooltips are usually anonHover component but I can see value on toggling it on and off onClick.

Hardcoding the direction might be problematic on some occasions. For example, on the desktop the tooltip might be better on the right, but on a smaller screen and depending on the layout, it might need a different alignment. So, a direction "auto" could be added, which would calculate the proper position depending on the element's position on the screen.

Love the idea of direction "auto". Maybe it doesn't even need to be a specific direction but a prop like autoAdjust or something like that. I think Intersection Observer API would be the right tool for it, or maybe something simpler with media-queries.

Since the tooltip can accept HTML, links, etc, it would be nice if it would stay open when moving the cursor above it. This could be handled with just CSS I think.

I haven't thought of that, nice call. I'm not sure if it is a CSS only thing though, as the event that triggers the tooltip hide is the wrapper onMouseLeave 🤔. If I had to implement only one of the suggestions I'd pick this one for sure.

Collapse
 
theandersonn profile image
Anderson Nascimento

Parabéns pelo tutorial Vitor!
O Tooltip ficou enxuto e muito funcional.

Me ajudou aqui na aplicação de um trabalho.

Como estou utilizando styled, fiz pequenas adaptações.
gist.github.com/theandersonn/9964e...

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

What does the fox say!!

Collapse
 
saulpalv profile image
Saul Alonso Palazuelos • Edited

you may want to use display: contents on the .Tooltip-Wrapperclass

Collapse
 
z2lai profile image
z2lai

I'm really liking your extremely reusable components series. Each article is extremely helpful!

Collapse
 
vtrpldn profile image
Vitor Paladini

Hey, z2lai! Thanks for the feedback, it warms my heart to know that they are being helpful. 😄