# Tooltip

> A React tooltip for icon buttons and cut-off text: shortcut hints, toolbar groups that skip the delay and a long press on touch.

- Type: React component (Overlays)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add tooltip`
- Docs: https://wingo-ui.com/components/tooltip

## Interaction

Rest the pointer on a trigger and after 300ms the label fades in, growing a touch out of the trigger. Inside a TooltipGroup only the first one waits: glide along a toolbar and each next label swaps in at once. Tab to a trigger and its tooltip opens straight away; Escape, a click, a scroll or moving away closes it. The pointer can travel onto the tooltip without closing it. A shortcut shows after the text in the notation of your device (⌘C on a Mac, Ctrl+C elsewhere) and is announced as aria-keyshortcuts. Phones have no hover, so a long press shows the label instead: the button does not fire, the tooltip stays while your finger is down and for 1.5 seconds after, and the next touch dismisses it. Shortcut hints hide on touch screens. With reduced motion it only fades.

## Installation

```bash
npx wingo-ui@latest add tooltip
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/tooltip.json`

Files:

- `components/ui/tooltip.tsx`

npm dependencies: `npm i @radix-ui/react-tooltip @radix-ui/react-slot motion`

Wingo UI dependencies (installed automatically):

- [Kbd](https://wingo-ui.com/components/kbd) ([Markdown](https://wingo-ui.com/components/kbd.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useLongPress](https://wingo-ui.com/components/use-long-press) ([Markdown](https://wingo-ui.com/components/use-long-press.md))
- [useShortcut](https://wingo-ui.com/components/use-shortcut) ([Markdown](https://wingo-ui.com/components/use-shortcut.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Copy, Heart, Share2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Tooltip, TooltipGroup } from "@/components/ui/tooltip"

export function ListingActions({ onSave, onCopy }: { onSave: () => void; onCopy: () => void }) {
  return (
    <TooltipGroup>
      <div className="flex items-center gap-1">
        <Tooltip content="Save to favorites" shortcut="s">
          <Button variant="ghost" size="icon" radius="full" aria-label="Save to favorites" onClick={onSave}>
            <Heart />
          </Button>
        </Tooltip>
        <Tooltip content="Share">
          <Button variant="ghost" size="icon" radius="full" aria-label="Share">
            <Share2 />
          </Button>
        </Tooltip>
        <Tooltip content="Copy link" shortcut="mod+c" side="bottom" arrow>
          <Button variant="ghost" size="icon" radius="full" aria-label="Copy link" onClick={onCopy}>
            <Copy />
          </Button>
        </Tooltip>
      </div>
    </TooltipGroup>
  )
}

// a value that only shows its tooltip when it is cut off
export function Address({ value }: { value: string }) {
  return (
    <Tooltip content={value} overflowOnly>
      <span tabIndex={0} className="block max-w-40 truncate">
        {value}
      </span>
    </Tooltip>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `content?` | `ReactNode` | `""` | The label: a few words for an icon button, or a sentence with size md. Empty renders the trigger alone, so a tooltip can come and go without re-mounting the button. |
| `shortcut?` | `string` | `""` | A combo in mod+k notation ("s", "mod+c", "alt+n", "shift+mod+p"), shown after the text at 60% of its color and set as aria-keyshortcuts on the trigger. Display only: bind the key yourself with useShortcut. Hidden on touch screens. |
| `children?` | `ReactNode` |  | The trigger, rendered asChild: a Button, an <a>, or a span with tabIndex={0}. It must pass a ref to its element. A disabled <button> gets no pointer events, so wrap it in a focusable span. A plain string gets a focusable span of its own. It keeps its own data-slot and data-state; the tooltip's state is in data-tooltip-state. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | neutral is the inverted tooltip: black on a light page, near-white on a dark one. Other tones fill with their color, for a label that carries meaning (out of stock, verified). |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone; the text turns black or white by lightness. Leave empty to use the tone. |
| `size?` | `"sm" \| "md"` | `"sm"` | sm is one short centered line in 12px medium type, up to 256px wide. md is for a sentence: 13px, more padding, left aligned, up to 288px wide. Both shrink to the room left on screen, so a flipped tooltip wraps on a phone instead of running off the edge. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner shape, scaled with the size: md is 8px on sm and 10px on md. full is a pill on one line and a soft capsule on a wrapped sentence. |
| `arrow?` | `boolean` | `false` | A small arrow with a rounded tip in the tooltip's color, pointing at the center of the trigger and kept clear of the corners. |
| `side?` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` | Where it opens. It flips to the opposite side and slides along the edge when there is no room. |
| `align?` | `"start" \| "center" \| "end"` | `"center"` | Alignment along the trigger's edge. |
| `sideOffset?` | `number` | `6` | Distance from the trigger in px. The arrow adds its own height on top. |
| `delayDuration?` | `number` | `300` | Hover time in ms before it opens. Keyboard focus opens it at once. Inside a TooltipGroup the group's delay wins (the playground feeds this value to the group). |
| `longPress?` | `boolean` | `true` | On touch screens a long press on the trigger shows the tooltip, with a light vibration where supported. The click that would follow is swallowed, so the button does not fire. Off: touch never shows it. |
| `touchDuration?` | `number` | `1500` | How long in ms it stays after the finger lifts from a long press. The next touch anywhere closes it sooner. |
| `overflowOnly?` | `boolean` | `false` | Opens only while the trigger's text is cut off (truncate or line-clamp on the trigger or inside it), so a cell shows its full value only when it needs to. The playground's heart never overflows, so it stays quiet. |
| `defaultOpen?` | `boolean` | `false` | Starts open. The next hover elsewhere, click or scroll closes it. |
| `disabled?` | `boolean` | `false` | Never opens and closes if it is open. The trigger itself keeps working. |
| `open?` | `boolean` |  | Controls it from outside, with onOpenChange. Handy for a confirmation like "Link copied" after a click. |
| `onOpenChange?` | `(open: boolean) => void` |  | Called on hover, focus, long press, Escape, click and scroll. Not called for opens that are refused (disabled, overflowOnly with nothing cut off, a tap on a touch screen). |
| `container?` | `HTMLElement \| null` |  | Where the portal mounts. document.body by default. |
| `classNames?` | `Partial<Record<"content" \| "arrow" \| "shortcut", string>>` |  | Extra classes for the tooltip body, the arrow svg and the shortcut Kbd. |
| `className?` | `string` |  | Extra classes for the tooltip body (the trigger is your child), merged last. Other Radix content props (alignOffset, collisionPadding, avoidCollisions, onEscapeKeyDown, aria-label) pass through. |

## Parts

### TooltipGroup (component)

Wrap a toolbar: the first tooltip waits delayDuration (300), then moving to the next within skipDelayDuration (300) opens it at once with a quick fade. Tooltips outside a group bring their own provider, so no setup is needed. Props: { delayDuration?: number; skipDelayDuration?: number; disableHoverableContent?: boolean; children }.

### TooltipRoot (component)

State and behavior for the parts: open, defaultOpen, onOpenChange, delayDuration, disabled, longPress, touchDuration, overflowOnly, shortcut, disableHoverableContent. Props: TooltipRootProps.

### TooltipTrigger (component)

Wires hover, focus and long press onto its child (asChild) or its own <button>, and sets aria-keyshortcuts. Props: ComponentProps<"button"> & { asChild?: boolean }.

### TooltipContent (component)

The portaled, animated body: side, align, sideOffset, arrow, tone, color, size, radius, container, classNames and the Radix content props. Renders the Root's shortcut after its children. Props: TooltipContentProps.

## Examples

- **Sides and alignments**: Four sides, three alignments, with the arrow. It flips and slides when there is no room.
- **Tones and a custom color**: Neutral is inverted in both themes; a tone or any CSS color for labels that carry meaning.
- **Shortcut hints**: The combo in the device's notation after the text; the app binds it. Try the keys.
- **On toggles**: The trigger keeps its own data-state, so a pressed toggle stays pressed under its tooltip. The shortcuts work too.
- **A sentence with size md**: 13px, left aligned, up to 288px wide: the fee explained next to a price summary. Touch screens show the note inline instead.
- **Cut-off values**: overflowOnly shows the full text only when truncate actually cut it.
- **On a disabled button**: A disabled button gets no pointer events: a focusable span carries the tooltip. Touch screens also show the reason inline.
- **Long press on a phone**: An icon-only photo toolbar. On touch a long press reads the label; touchDuration keeps it 2s.

## See also

- [Kbd](https://wingo-ui.com/components/kbd) ([Markdown](https://wingo-ui.com/components/kbd.md))
- [Hover Card](https://wingo-ui.com/components/hover-card) ([Markdown](https://wingo-ui.com/components/hover-card.md))
- [Popover](https://wingo-ui.com/components/popover) ([Markdown](https://wingo-ui.com/components/popover.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [useLongPress](https://wingo-ui.com/components/use-long-press) ([Markdown](https://wingo-ui.com/components/use-long-press.md))
- [useShortcut](https://wingo-ui.com/components/use-shortcut) ([Markdown](https://wingo-ui.com/components/use-shortcut.md))

---

Canonical: https://wingo-ui.com/components/tooltip
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
