DEV Community

Cover image for Build your own React Tooltip Component
Alex Suarez
Alex Suarez

Posted on

Build your own React Tooltip Component

Hello! Happy Samhain/Halloween!!! This a very spooky fast article where I would like to show you how I built a react tooltip on my latest side-project.

This is a simple Tooltip component, and even I know there are great libs out there, I'm always betting on building mine first before moving and add a new dependency to my project, let's go...

I'm using tailwindcss for this one, if you are not using tailwindcss in your React project please stop right now and go to https://tailwindcss.com/ and take a look, I have also a couple of starters on github for CRA and Nextjs you may want to clone for a quick start:
Nextjs -> https://github.com/alexandprivate/next-netlify-tailwind-starter
CRA -> https://github.com/alexandprivate/cra-tailwind-router-starter

Now do let's see the code

Full component

function Tooltip({ children, tooltipText }) {
    const tipRef = React.createRef(null);
    function handleMouseEnter() {
        tipRef.current.style.opacity = 1;
        tipRef.current.style.marginLeft = "20px";
    }
    function handleMouseLeave() {
        tipRef.current.style.opacity = 0;
        tipRef.current.style.marginLeft = "10px";
    }
    return (
        <div
            className="relative flex items-center"
            onMouseEnter={handleMouseEnter}
            onMouseLeave={handleMouseLeave}
        >
            <div
                className="absolute whitespace-no-wrap bg-gradient-to-r from-black to-gray-700 text-white px-4 py-2 rounded flex items-center transition-all duration-150"
                style={{ left: "100%", opacity: 0 }}
                ref={tipRef}
            >
                <div
                    className="bg-black h-3 w-3 absolute"
                    style={{ left: "-6px", transform: "rotate(45deg)" }}
                />
                {tooltipText}
            </div>
            {children}
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

A couple of clarifications, the tooltip always opens to the right in this case but you can always tweak the direction or even create a prop to control it

I'm using ref to avoid setting a state to show or hide the tooltip, these ways the component doesn't have to deal with re-renders

const tipRef = React.createRef(null);
Enter fullscreen mode Exit fullscreen mode

and handling the show and hide events with onMouse API

    function handleMouseEnter() {
        tipRef.current.style.opacity = 1;
        tipRef.current.style.marginLeft = "20px";
    }
    function handleMouseLeave() {
        tipRef.current.style.opacity = 0;
        tipRef.current.style.marginLeft = "10px";
    }
Enter fullscreen mode Exit fullscreen mode

There is a prop for the tooltip text and children to use this as a composition, so you may use the component like this

Now you just need to wrap an element where you want to show the tooltip at, for example on a Nextjs Link

    <Tooltip tooltipText="Shop Insights">
          <Link href="/insights">
                 <a>
                    <AiOutlineAlert className="text-3xl" />
                 </a>
          </Link>
    </Tooltip>
Enter fullscreen mode Exit fullscreen mode

And you will get something like this...

ezgif.com-gif-maker

Happy coding and don't eat too many candies today!

Top comments (4)

Collapse
 
germansaracca profile image
German Gonzalo Saracca • Edited

Hi Alex, thank you for this beautiful piece of code. Here I leave the code ready to receive in which position to appear.

`import { useRef } from 'react'
import propTypes from 'prop-types'

const Tooltip = ({ children, tooltipText, orientation = 'right' }) => {
const tipRef = useRef(null)

const orientations = {
    right: 'right',
    top: 'top',
    left: 'left',
    bottom: 'bottom',
}

function handleMouseEnter() {
    tipRef.current.style.opacity = 1
}

function handleMouseLeave() {
    tipRef.current.style.opacity = 0
}

const setContainerPosition = (orientation) => {
    let classnames

    switch (orientation) {
        case orientations.right:
            classnames = 'top-0 left-full ml-4'
            break
        case orientations.left:
            classnames = 'top-0 right-full mr-4'
            break
        case orientations.top:
            classnames = 'bottom-full left-[50%] translate-x-[-50%] -translate-y-2'
            break
        case orientations.bottom:
            classnames = 'top-full left-[50%] translate-x-[-50%] translate-y-2'
            break

        default:
            break
    }

    return classnames
}

const setPointerPosition = (orientation) => {
    let classnames

    switch (orientation) {
        case orientations.right:
            classnames = 'left-[-6px]'
            break
        case orientations.left:
            classnames = 'right-[-6px]'
            break
        case orientations.top:
            classnames = 'top-full left-[50%] translate-x-[-50%] -translate-y-2'
            break
        case orientations.bottom:
            classnames = 'bottom-full left-[50%] translate-x-[-50%] translate-y-2'
            break

        default:
            break
    }

    return classnames
}

const classContainer = `w-max absolute z-10 ${setContainerPosition(
    orientation
)} bg-gray-600 text-white text-sm px-2 py-1 rounded flex items-center transition-all duration-150 pointer-events-none`

const pointerClasses = `bg-gray-600 h-3 w-3 absolute z-10 ${setPointerPosition(
    orientation
)} rotate-45 pointer-events-none`

return (
    <div className="relative flex items-center" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
        <div className={classContainer} style={{ opacity: 0 }} ref={tipRef}>
            <div className={pointerClasses} />
            {tooltipText}
        </div>
        {children}
    </div>
)
Enter fullscreen mode Exit fullscreen mode

}

Tooltip.propTypes = {
orientation: propTypes.oneOf(['top', 'left', 'right', 'bottom']),
tooltipText: propTypes.string.isRequired,
}
export default Tooltip`

Collapse
 
yoursunny profile image
Junxiao Shi

It's a lot easier to use the HTML title attribute:

<button title="view details">details</button>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
alexandprivate profile image
Alex Suarez • Edited

Hi there, LOL, easier? Sure. Better? Absolutely not, you can style title, not ui or behaviour, also title has a default delay to show up, we can say title is the most primitive way of tooltip natively support by the browser, so if you can improve that do it 👍

Collapse
 
obiwarn profile image
Sebastian Obentheuer

Fantastic. Thank you!