ComponentsTooltip

Tooltip

Add to library
Terminal
npm i framer-motion clsx tailwind-merge
utils/cn.ts
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
Tooltip.tsx
"use client"; // @NOTE: add in case you are using Next.js

import { useState } from "react";
import { motion } from "framer-motion";

import { cn } from "@/utils/cn";

export function TooltipExample() {
  return (
    <Tooltip text="Add to library">
      <button className="rounded-xl border border-neutral-900 bg-neutral-950 px-4 py-2 text-sm font-medium">
        Hover Me
      </button>
    </Tooltip>
  );
}

type TooltipProps = {
  text: string;
} & React.ComponentProps<"div">;

function Tooltip({ text, children, className }: TooltipProps) {
  const [isToastVisible, setIsToastVisible] = useState(false);

  return (
    <div
      onMouseEnter={() => setIsToastVisible(true)}
      onMouseLeave={() => setIsToastVisible(false)}
      className="relative inline-block"
    >
      <motion.div
        className={cn(
          "absolute -top-4 left-1/2 whitespace-nowrap rounded-md border border-border bg-neutral-900 px-2 py-1 text-xs text-white shadow [translate:-50%_-50%]",
          className,
        )}
        initial={{ opacity: 0, y: 5, filter: "blur(4px)", scale: 0.9 }}
        animate={{
          opacity: isToastVisible ? 1 : 0,
          y: isToastVisible ? 0 : 5,
          filter: isToastVisible ? "blur(0px)" : "blur(4px)",
          scale: isToastVisible ? 1 : 0.9,
        }}
        transition={{ ease: "easeInOut", duration: 0.15 }}
      >
        <span>{text}</span>
      </motion.div>

      {children}
    </div>
  );
}