Build websites 10x faster with HextaUI Blocks — Learn more
UI/UI

Tooltip

A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.

<TooltipProvider>
  <div className="flex gap-4 flex-wrap items-center">
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="outline">Hover me</Button>
      </TooltipTrigger>
      <TooltipContent>
        <p>This is a tooltip</p>
      </TooltipContent>
    </Tooltip>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="default">Dark tooltip</Button>
      </TooltipTrigger>
      <TooltipContent variant="dark">
        <p>Dark variant tooltip</p>
      </TooltipContent>
    </Tooltip>
  </div>
</TooltipProvider>

Installation

Install following dependencies:

npm install @radix-ui/react-tooltip motion class-variance-authority
pnpm add @radix-ui/react-tooltip motion class-variance-authority
yarn add @radix-ui/react-tooltip motion class-variance-authority
bun add @radix-ui/react-tooltip motion class-variance-authority

Copy and paste the following code into your project.

components/ui/tooltip.tsx
"use client";

import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cva, type VariantProps } from "class-variance-authority";
import { motion, AnimatePresence } from "motion/react";
import { cn } from "@/lib/utils";

const tooltipVariants = cva(
  "z-50 overflow-hidden rounded-lg border border-[hsl(var(--hu-border))] bg-[hsl(var(--hu-card-background))] px-3 py-1.5 text-xs text-[hsl(var(--hu-card-foreground))] font-medium",
  {
    variants: {
      variant: {
        default:
          "bg-[hsl(var(--hu-card-background))] text-[hsl(var(--hu-card-foreground))]",
        dark: "bg-[hsl(var(--hu-foreground))] text-[hsl(var(--hu-background))] border-[hsl(var(--hu-foreground))]",
        light:
          "bg-[hsl(var(--hu-background))] text-[hsl(var(--hu-foreground))] border-[hsl(var(--hu-border))]",
        destructive:
          "bg-[hsl(var(--hu-destructive))] text-[hsl(var(--hu-primary-foreground))] border-[hsl(var(--hu-destructive))]",
      },
      size: {
        sm: "px-2 py-1 text-xs",
        md: "px-3 py-1.5 text-sm",
        lg: "px-4 py-2 text-base",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "md",
    },
  },
);

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipProvider = TooltipPrimitive.Provider;

interface TooltipContentProps
  extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>,
    VariantProps<typeof tooltipVariants> {}

interface TooltipProviderProps
  extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Provider> {}

const TooltipContent = React.forwardRef<
  React.ElementRef<typeof TooltipPrimitive.Content>,
  TooltipContentProps
>(({ className, variant, size, sideOffset = 4, ...props }, ref) => {
  const [isVisible, setIsVisible] = React.useState(false);

  return (
    <AnimatePresence>
      <TooltipPrimitive.Content
        ref={ref}
        sideOffset={sideOffset}
        className={cn("relative", className)}
        onAnimationStart={() => setIsVisible(true)}
        onAnimationEnd={() => setIsVisible(false)}
        asChild
        {...props}
      >
        <motion.div
          initial={{ opacity: 0, scale: 0.8, y: 5 }}
          animate={{ opacity: 1, scale: 1, y: 0 }}
          exit={{ opacity: 0, scale: 0.8, y: 5 }}
          transition={{
            type: "spring",
            stiffness: 300,
            damping: 20,
            duration: 0.2,
          }}
          className={cn(tooltipVariants({ variant, size }), className)}
        >
          {props.children}
        </motion.div>
      </TooltipPrimitive.Content>
    </AnimatePresence>
  );
});
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export {
  Tooltip,
  TooltipTrigger,
  TooltipContent,
  TooltipProvider,
  tooltipVariants,
  type TooltipContentProps,
  type TooltipProviderProps,
};
npx hextaui@latest add tooltip
pnpm dlx hextaui@latest add tooltip
yarn dlx hextaui@latest add tooltip
bun x hextaui@latest add tooltip

Usage

import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/Tooltip";
<TooltipProvider>
  <Tooltip>
    <TooltipTrigger>Hover me</TooltipTrigger>
    <TooltipContent>
      <p>This is a tooltip</p>
    </TooltipContent>
  </Tooltip>
</TooltipProvider>

Examples

Default

<TooltipProvider>
  <Tooltip>
    <TooltipTrigger asChild>
      <Button variant="outline">Default tooltip</Button>
    </TooltipTrigger>
    <TooltipContent>
      <p>This is a default tooltip</p>
    </TooltipContent>
  </Tooltip>
</TooltipProvider>

Variants

<TooltipContent variant="default">Default tooltip</TooltipContent>
<TooltipContent variant="dark">Dark tooltip</TooltipContent>
<TooltipContent variant="light">Light tooltip</TooltipContent>
<TooltipContent variant="destructive">Destructive tooltip</TooltipContent>

Sizes

<TooltipContent size="sm">Small tooltip</TooltipContent>
<TooltipContent size="md">Medium tooltip</TooltipContent>
<TooltipContent size="lg">Large tooltip</TooltipContent>

Positioning

<TooltipContent side="top">Tooltip on top</TooltipContent>
<TooltipContent side="right">Tooltip on right</TooltipContent>
<TooltipContent side="bottom">Tooltip on bottom</TooltipContent>
<TooltipContent side="left">Tooltip on left</TooltipContent>

Custom Delay

<TooltipProvider delayDuration={300}>
  <Tooltip>
    <TooltipTrigger asChild>
      <Button>Fast tooltip</Button>
    </TooltipTrigger>
    <TooltipContent>
      <p>Fast tooltip delay</p>
    </TooltipContent>
  </Tooltip>
</TooltipProvider>

<Tooltip delayDuration={1000}>
  <TooltipTrigger asChild>
    <Button>Slow tooltip</Button>
  </TooltipTrigger>
  <TooltipContent>
    <p>Slow tooltip delay</p>
  </TooltipContent>
</Tooltip>

Rich Content

<TooltipProvider>
  <div className="flex gap-4 flex-wrap">
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="outline">Rich content</Button>
      </TooltipTrigger>
      <TooltipContent size="lg" className="max-w-xs">
        <div className="space-y-2">
          <div className="font-medium">Feature Description</div>
          <div className="text-sm opacity-80">
            This is a more detailed explanation of what this feature does
            and how it works.
          </div>
          <div className="flex gap-1">
            <span className="text-xs bg-[hsl(var(--hu-secondary))] px-1.5 py-0.5 rounded">
              New
            </span>
            <span className="text-xs bg-[hsl(var(--hu-accent))] px-1.5 py-0.5 rounded">
              Beta
            </span>
          </div>
        </div>
      </TooltipContent>
    </Tooltip>
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="outline">Keyboard shortcut</Button>
      </TooltipTrigger>
      <TooltipContent>
        <div className="flex items-center gap-1">
          <span>Save file</span>
          <Kbd className="px-1 py-0.5 text-xs bg-[hsl(var(--hu-secondary))] rounded">
            Ctrl+S
          </Kbd>
        </div>
      </TooltipContent>
    </Tooltip>
  </div>
</TooltipProvider>

Props

PropTypeDefault
className?
string
undefined
sideOffset?
number
4
side?
"top" | "right" | "bottom" | "left"
"top"
size?
"sm" | "md" | "lg"
"md"
variant?
"default" | "dark" | "light" | "destructive"
"default"
Edit on GitHub

Last updated on