Tooltip

Auto-flipping dark tooltip that wraps any element, shown on hover and focus.

import { Tooltip, TooltipProps }from "@/components/ui"

On a button

Full source

The complete component, read from src/components/ui/tooltip.tsx.

tooltip.tsx
"use client";

import { useState, useRef } from "react";
import { createPortal } from "react-dom";
import { usePopoverPosition } from "@/hooks/use-popover-position";
import { useMounted } from "@/hooks/use-mounted";

export interface TooltipProps {

  content: React.ReactNode;
  children: React.ReactElement;

  delayMs?: number;
}

export function Tooltip({ content, children, delayMs = 400 }: TooltipProps) {
  const [open, setOpen] = useState(false);
  const anchorRef = useRef<HTMLSpanElement>(null);
  const mounted = useMounted();
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const position = usePopoverPosition(anchorRef, open, {
    align: "center",
    gap: 6,
    minWidth: 60,
    preferredHeight: 40,
  });

  const show = () => {
    timerRef.current = setTimeout(() => setOpen(true), delayMs);
  };
  const hide = () => {
    if (timerRef.current) clearTimeout(timerRef.current);
    setOpen(false);
  };

  return (
    <>
      <span
        ref={anchorRef}
        className="inline-flex"
        onMouseEnter={show}
        onMouseLeave={hide}
        onFocus={show}
        onBlur={hide}
      >
        {children}
      </span>

      {open && position && mounted &&
        createPortal(
          <span
            role="tooltip"
            className="pointer-events-none z-50 max-w-56 whitespace-nowrap rounded-md bg-surface-dark px-2.5 py-1.5 text-xs leading-snug text-on-dark shadow-lg"
            style={position.style}
          >
            {content}
            <span
              aria-hidden="true"
              className="absolute left-1/2 size-2 -translate-x-1/2 rotate-45 bg-surface-dark"
              style={position.placement === "top" ? { bottom: -4 } : { top: -4 }}
            />
          </span>,
          document.body
        )}
    </>
  );
}

Dependencies

To use Tooltip you also need the following. Copy these into your project alongside it.

Recommended folder structure

project structure
src/components/ui/
  └─ tooltip.tsx
src/hooks/
  ├─ use-mounted.ts
  └─ use-popover-position.ts
src/lib/
  └─ utils.ts