Skip to content
txbrendan/ui

Tooltip

beta

A short text label that shows on hover or keyboard focus, to name an icon button or show a cut-off label in full.

Preview

Effect

icons

shortcut

cut off

side

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/tooltip

Use it for

  • Naming an icon-only button for sighted mouse and keyboard users, like Bold in a toolbar.
  • Showing the full text of a label that is cut off with an ellipsis.
  • Wrap a row of tooltips in one TooltipProvider. After the first one opens, the next one opens at once.
  • The tooltip opens at the end of the page body. In a box with its own theme, like a dark preview on a light page, pass that box as container on TooltipPopup so the tooltip keeps its colors. A container that clips its overflow can clip the tooltip.
  • Teaching a keyboard shortcut. Pass the key to TooltipPopup's shortcut, and put the same key in aria-keyshortcuts on the trigger. Your app binds the key.
  • On a text button, the tooltip can be the key alone: <TooltipPopup shortcut="⌘S" />. The button already says what it does.
  • While a menu, list, popover, or peek is open, a tip that opens outside it stays out of sight until it closes, so two popups never show at once. A tip inside the open popup shows as usual.

Avoid it for

  • Anything people must see to use the page. Touch screens never show tooltips, and screen readers do not read them. Put it in visible text.
  • Links, buttons, or fields inside it. A tooltip closes when the pointer leaves. Use a popover.
  • An info icon that opens help text. Use a popover, so touch and screen reader users get the text too.
  • More than a few words. Use a popover for longer text.
  • An error for a form field. Show it next to the field.

Props

  • Tooltip

    Root. Holds one tooltip's open state and renders no element. Takes open, defaultOpen, and onOpenChange.

    { open?: boolean; defaultOpen?: boolean; onOpenChange?: (open: boolean, details) => void }

  • open

    Tooltip. Whether it shows, controlled. Use it with onOpenChange.

    boolean

  • defaultOpen

    Tooltip. Whether it shows at the start, when you do not control it.

    boolean · default false

  • onOpenChange

    Tooltip. Called with the new open state when it opens or closes.

    (open: boolean, details) => void

  • TooltipProvider

    Wrap a group of tooltips, like a toolbar. After the first one opens, the next one opens at once. Each trigger's delay sets the first wait.

    { closeDelay?: number; timeout?: number }

  • TooltipTrigger

    Part. The element the tooltip names. A <button> by default. Pass render={<Button />} to use your own button.

    ComponentProps<"button"> & { render?: ReactElement }

  • TooltipPopup

    Part. The tooltip text. It renders in a portal, above the page, while open.

    ComponentProps<"div">

  • side

    TooltipPopup. Which side of the trigger it opens on. It flips when there is no room.

    "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" · default "top"

  • align

    TooltipPopup. How it lines up with the trigger.

    "start" | "center" | "end" · default "center"

  • sideOffset

    TooltipPopup. Gap between the trigger and the tooltip, in pixels.

    number · default 6

  • shortcut

    TooltipPopup. The key that runs the trigger's action, like "⌘B". It shows after the text in the muted color. It only shows the key. Your app binds it.

    ReactNode

  • collisionPadding

    TooltipPopup. Least gap between the tooltip and the screen edge, in pixels. It keeps the tooltip off the edge on phones.

    number | { top?: number; right?: number; bottom?: number; left?: number } · default 8

  • container

    TooltipPopup. The element the tooltip opens in. By default, the end of the page body, so no parent can clip it. Pass an element, or a ref itself (not ref.current), to open it inside, for example to keep a parent's theme. Only client code has elements and refs. A null container shows no tooltip.

    HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null

  • effect

    TooltipPopup. How the tooltip shows and hides. rise fades in and moves out from the trigger. grow also grows from the trigger, and closes the same way. instant shows and hides at once, with no motion. Left out, it follows an effect-<name> class on <body>, where popups open, and rises with none.

    "rise" | "grow" | "instant" · default "rise"

  • delay

    TooltipTrigger. How long the pointer must rest on the trigger before the tooltip opens, in milliseconds.

    number · default 150

  • disabled

    Tooltip or TooltipTrigger. Stops the tooltip from opening. It does not disable the trigger itself.

    boolean · default false

  • className

    TooltipPopup. Extra classes. Later classes win, so className='rounded-none' replaces the radius.

    string

Accessibility

  • Tooltips are visual only. The tooltip is not linked to its trigger for screen readers, so the trigger needs its own name.
  • For an icon-only button, give it an aria-label that says the same as the tooltip. Button's iconOnly type makes aria-label required.
  • For a cut-off label, the trigger's own text is the full label, so screen readers read all of it.
  • It opens after a short hover, or at once on keyboard focus. Escape closes it.
  • A tap does not open it. A tooltip opens only on mouse hover or keyboard focus, so touch screen users never see it.
  • To show a tooltip on a disabled button, give the Button focusableWhenDisabled. It can then still get focus and hover.
  • The theme tests hold tooltip text to 4.5:1 on the popover color, in light and dark. The shortcut uses the muted text color, which they hold to 4.5:1 on the popover color too.
  • A shortcut in the tooltip is not read out, since the tooltip is visual only. Put the key in aria-keyshortcuts on the trigger, like aria-keyshortcuts="Meta+B", so screen readers say it.

Examples

Icon buttons in a toolbar (needs @txbrendan/button)

import { Bold, Italic } from "lucide-react";

import { Button } from "@/components/ui/button";

<TooltipProvider>
  <Tooltip>
    <TooltipTrigger render={<Button iconOnly emphasis="ghost" aria-label="Bold" />}>
      <Bold />
    </TooltipTrigger>
    <TooltipPopup>Bold</TooltipPopup>
  </Tooltip>
  <Tooltip>
    <TooltipTrigger render={<Button iconOnly emphasis="ghost" aria-label="Italic" />}>
      <Italic />
    </TooltipTrigger>
    <TooltipPopup>Italic</TooltipPopup>
  </Tooltip>
</TooltipProvider>

Teach a key in the tooltip (needs @txbrendan/button)

import { Bold } from "lucide-react";

import { Button } from "@/components/ui/button";

// Your app listens for ⌘B and runs the same action. The tooltip only teaches the key.
<div className="flex gap-2">
  <Tooltip>
    <TooltipTrigger render={<Button iconOnly emphasis="ghost" aria-label="Bold" aria-keyshortcuts="Meta+B" />}>
      <Bold />
    </TooltipTrigger>
    <TooltipPopup shortcut="⌘B">Bold</TooltipPopup>
  </Tooltip>
  <Tooltip>
    <TooltipTrigger render={<Button emphasis="outline" aria-keyshortcuts="Meta+S" />}>Save draft</TooltipTrigger>
    <TooltipPopup shortcut="⌘S" />
  </Tooltip>
</div>

The full text of a cut-off label

function FileName({ name }: { name: string }) {
  return (
    <Tooltip>
      <TooltipTrigger className="max-w-48 truncate">{name}</TooltipTrigger>
      <TooltipPopup>{name}</TooltipPopup>
    </Tooltip>
  );
}

Below the trigger (needs @txbrendan/button)

import { Button } from "@/components/ui/button";

<Tooltip>
  <TooltipTrigger render={<Button emphasis="outline" />}>Share</TooltipTrigger>
  <TooltipPopup side="bottom">Copy a link to this page</TooltipPopup>
</Tooltip>

Shows at once, with no motion (needs @txbrendan/button)

import { Button } from "@/components/ui/button";

<Tooltip>
  <TooltipTrigger render={<Button emphasis="outline" />}>Search</TooltipTrigger>
  <TooltipPopup effect="instant" shortcut="⌘K" />
</Tooltip>