Tooltip

Lightweight hover tooltip with arrow caret — supports top, bottom, left, and right positioning with dark mode inversion.

Preview

Basic tooltip

import { Tooltip } from '@/registry/ui-kit/tailwind-tooltip/react'

function Example() {
  return (
    <Tooltip content="Hello world" position="top">
      <button className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white">
        Hover me
      </button>
    </Tooltip>
  )
}

Component API

PropDefaultDescription
contentThe text or element displayed inside the tooltip.
position"top"Which side of the trigger the tooltip appears on.
childrenThe trigger element that activates the tooltip on hover.

Source Code

"use client";

import React, { useState } from "react";

const positionClasses: Record<string, string> = {
  top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
  bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
  left: "right-full top-1/2 -translate-y-1/2 mr-2",
  right: "left-full top-1/2 -translate-y-1/2 ml-2",
};

const arrowClasses: Record<string, string> = {
  top: "top-full left-1/2 -translate-x-1/2 border-t-zinc-900 dark:border-t-zinc-100 border-l-transparent border-r-transparent border-b-transparent",
  bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-zinc-900 dark:border-b-zinc-100 border-l-transparent border-r-transparent border-t-transparent",
  left: "left-full top-1/2 -translate-y-1/2 border-l-zinc-900 dark:border-l-zinc-100 border-t-transparent border-b-transparent border-r-transparent",
  right: "right-full top-1/2 -translate-y-1/2 border-r-zinc-900 dark:border-r-zinc-100 border-t-transparent border-b-transparent border-l-transparent",
};

/* ── Tooltip ────────────────────────────────────────── */
export function Tooltip({
  content,
  position = "top",
  children,
  className = "",
}: {
  content: React.ReactNode;
  position?: "top" | "bottom" | "left" | "right";
  children: React.ReactNode;
  className?: string;
}) {
  const [visible, setVisible] = useState(false);

  return (
    <div
      className={`relative inline-block ${className}`}
      onMouseEnter={() => setVisible(true)}
      onMouseLeave={() => setVisible(false)}
    >
      {children}
      {visible && (
        <div className={`absolute z-10 ${positionClasses[position]}`}>
          <div className="whitespace-nowrap rounded-md bg-zinc-900 px-2.5 py-1.5 text-xs font-medium text-white shadow-lg dark:bg-zinc-100 dark:text-zinc-900">
            {content}
            {/* Arrow */}
            <span
              className={`absolute h-0 w-0 border-4 ${arrowClasses[position]}`}
            />
          </div>
        </div>
      )}
    </div>
  );
}