Skip to content
Dethink Components

ComponentsTooltip

Tooltip

Show a short hint when users hover over or focus a control.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@dethink/components";

Try the examples, then open the code to use them in your app. Tab to a trigger — tooltips show on focus too.

Basic

An icon-only trigger keeps its aria-label; the tooltip adds the visible hint.

Show sourceexamples/tooltip/basic.tsx
examples/tooltip/basic.tsx
"use client";

import { Tooltip, TooltipContent, TooltipTrigger } from "@dethink/components";
import { RefreshCw } from "lucide-react";

export function TooltipBasic() {
  return (
    <div className="flex justify-center">
      <Tooltip>
        <TooltipTrigger
          aria-label="Refresh dashboard"
          size="icon"
          variant="outline"
        >
          <RefreshCw className="size-4" aria-hidden="true" />
        </TooltipTrigger>
        <TooltipContent>Refresh dashboard data</TooltipContent>
      </Tooltip>
    </div>
  );
}

Timing and placement

delay and closeDelay tune hover intent; placement and showArrow position the hint.

Show sourceexamples/tooltip/timing.tsx
examples/tooltip/timing.tsx
"use client";

import { Tooltip, TooltipContent, TooltipTrigger } from "@dethink/components";

export function TooltipTiming() {
  return (
    <div className="flex flex-wrap justify-center gap-3">
      <Tooltip delay={0} closeDelay={0}>
        <TooltipTrigger variant="soft" size="sm">
          Instant
        </TooltipTrigger>
        <TooltipContent>Shows immediately</TooltipContent>
      </Tooltip>
      <Tooltip delay={700} closeDelay={300}>
        <TooltipTrigger variant="soft" size="sm">
          Patient
        </TooltipTrigger>
        <TooltipContent>700ms open delay, 300ms close delay</TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger variant="soft" size="sm">
          Placed top
        </TooltipTrigger>
        <TooltipContent placement="top" showArrow>
          Anchored above with an arrow
        </TooltipContent>
      </Tooltip>
    </div>
  );
}

Examples that combine components for common tasks.

Formatting toolbar with shortcut hints

Tooltips in their strongest seat: an icon-only toolbar where each hint pairs the action name with its keyboard shortcut, while aria-label and aria-pressed keep the buttons fully accessible without the tooltip.

Show sourceexamples/tooltip/recipe-toolbar.tsx
examples/tooltip/recipe-toolbar.tsx
"use client";

import { useState } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@dethink/components";
import { Bold, Italic, Link2, Strikethrough } from "lucide-react";

const tools = [
  { id: "bold", label: "Bold", keys: "⌘B", icon: Bold },
  { id: "italic", label: "Italic", keys: "⌘I", icon: Italic },
  { id: "strike", label: "Strikethrough", keys: "⌘⇧X", icon: Strikethrough },
  { id: "link", label: "Add link", keys: "⌘K", icon: Link2 },
];

/**
 * Icon-only toolbars are where tooltips earn their keep: each trigger keeps
 * a real aria-label (the tooltip is a hint, not the accessible name) and the
 * content pairs the label with its keyboard shortcut.
 */
export function TooltipRecipeToolbar() {
  const [active, setActive] = useState<Set<string>>(new Set(["bold"]));

  return (
    <div
      role="toolbar"
      aria-label="Text formatting"
      className="border-border bg-muted/40 mx-auto flex w-fit gap-1 rounded-lg border p-1"
    >
      {tools.map((tool) => (
        <Tooltip key={tool.id} delay={300}>
          <TooltipTrigger
            aria-label={tool.label}
            aria-pressed={active.has(tool.id)}
            size="icon"
            variant={active.has(tool.id) ? "soft" : "ghost"}
            onClick={() =>
              setActive((current) => {
                const next = new Set(current);
                if (next.has(tool.id)) {
                  next.delete(tool.id);
                } else {
                  next.add(tool.id);
                }
                return next;
              })
            }
          >
            <tool.icon className="size-4" aria-hidden="true" />
          </TooltipTrigger>
          <TooltipContent placement="top">
            <span className="flex items-center gap-2">
              {tool.label}
              <kbd className="border-border/60 rounded border px-1 font-mono text-[0.7em]">
                {tool.keys}
              </kbd>
            </span>
          </TooltipContent>
        </Tooltip>
      ))}
    </div>
  );
}

Tooltip coordinates timing; the trigger is a real Button and the content carries placement.

Tooltip anatomy
PropWhat it doesDefault
delay / closeDelaynumber (ms)Hover intent timing before showing and hiding.library defaults
open / onOpenChangeboolean / (open) => voidControlled visibility for programmatic tooltips.Not set
disabledbooleanSuppresses the tooltip without disabling the trigger.false
TooltipTriggervariant + size (Button API)A real button trigger — keep the accessible name on the trigger (aria-label); the tooltip is a hint, not the name.Not set
TooltipContent — placement"top" | "bottom" | "left" | "right" (+ alignment)Preferred side relative to the trigger."top"
TooltipContent — showArrowbooleanRenders a pointing arrow.false