# Tooltip

A short label that appears on hover or focus.

Source: [tooltip.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/tooltip.tsx)

```tsx
import { ArchiveIcon, CopyIcon, PencilIcon, StarIcon, Trash2Icon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

const actions = [
  { label: "Edit", icon: PencilIcon },
  { label: "Duplicate", icon: CopyIcon },
  { label: "Star", icon: StarIcon },
  { label: "Archive", icon: ArchiveIcon },
  { label: "Delete", icon: Trash2Icon },
];

export default function TooltipIconButtons() {
  return (
    <div className="flex items-center gap-1">
      {actions.map(({ label, icon: Icon }) => (
        <Tooltip key={label}>
          <TooltipTrigger render={<Button variant="ghost" size="icon" aria-label={label} />}>
            <Icon />
          </TooltipTrigger>
          <TooltipContent>{label}</TooltipContent>
        </Tooltip>
      ))}
    </div>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/tooltip
```

## Usage

Wrap your app in a `TooltipProvider` once, near its root.

```tsx
import type { ReactNode } from "react";

import { TooltipProvider } from "@/components/ui/tooltip";

export function App({ children }: { children: ReactNode }) {
  return <TooltipProvider>{children}</TooltipProvider>;
}
```

Then give each control its own tooltip.

```tsx
import { PencilIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export function EditButton() {
  return (
    <Tooltip>
      <TooltipTrigger render={<Button variant="ghost" size="icon" aria-label="Edit" />}>
        <PencilIcon />
      </TooltipTrigger>
      <TooltipContent>Edit</TooltipContent>
    </Tooltip>
  );
}
```

Screen readers ignore tooltips, so the trigger still needs its own accessible name, like an
`aria-label`. Don't put anything essential in a tooltip. For an info icon, use a Popover
[infotip](https://ui.jarv.is/components/popover.md) instead.

## Composition

- `TooltipProvider`
  - `Tooltip`
    - `TooltipTrigger`
    - `TooltipContent`

## Examples

### With a shortcut

```tsx
import { SearchIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function TooltipWithShortcut() {
  return (
    <Tooltip>
      <TooltipTrigger render={<Button variant="secondary" size="icon" aria-label="Search" />}>
        <SearchIcon />
      </TooltipTrigger>
      <TooltipContent>
        Search
        <KbdGroup>
          <Kbd>⌘</Kbd>
          <Kbd>K</Kbd>
        </KbdGroup>
      </TooltipContent>
    </Tooltip>
  );
}
```

### Sides

`side` and `align` on `TooltipContent` set its position.

```tsx
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

const sides = ["top", "right", "bottom", "left"] as const;

export default function TooltipSides() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {sides.map((side) => (
        <Tooltip key={side}>
          <TooltipTrigger render={<Button variant="outline" size="sm" className="capitalize" />}>
            {side}
          </TooltipTrigger>
          <TooltipContent side={side}>Shown on the {side}</TooltipContent>
        </Tooltip>
      ))}
    </div>
  );
}
```

### Disabled triggers

A tooltip can explain why a control is disabled. Add `focusableWhenDisabled` so the button still
gets focus and taps, and give it the reason in `aria-description` for screen readers.

```tsx
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function TooltipDisabled() {
  return (
    <Tooltip>
      <TooltipTrigger
        render={
          <Button
            variant="primary"
            disabled
            focusableWhenDisabled
            aria-description="Add a title to publish"
          />
        }
      >
        Publish
      </TooltipTrigger>
      <TooltipContent>Add a title to publish</TooltipContent>
    </Tooltip>
  );
}
```

### Opening on tap

Tooltips don't open on touch screens unless the trigger is disabled. `openOnTap` opens one on tap
anyway, and `openOnTap={false}` turns it off for a disabled trigger.

```tsx
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function TooltipOpenOnTap() {
  return (
    <p className="text-sm text-muted-foreground">
      Edited{" "}
      <Tooltip>
        <TooltipTrigger
          openOnTap
          render={<time dateTime="2026-03-04T15:12" />}
          className="underline decoration-border-strong decoration-dotted underline-offset-2"
        >
          2 hours ago
        </TooltipTrigger>
        <TooltipContent>March 4, 2026 at 3:12 PM</TooltipContent>
      </Tooltip>
    </p>
  );
}
```

## Props

| Part | Extends |
| --- | --- |
| `TooltipProvider` | [`Tooltip.Provider`](https://base-ui.com/react/components/tooltip#provider) |
| `Tooltip` | [`Tooltip.Root`](https://base-ui.com/react/components/tooltip#root) |
| `TooltipTrigger` | [`Tooltip.Trigger`](https://base-ui.com/react/components/tooltip#trigger) |
| `TooltipContent` | [`Tooltip.Popup`](https://base-ui.com/react/components/tooltip#popup) |

The tables leave out `className`, `style` and `render`. Parts built on Base UI take them as its [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides describe.

### TooltipProvider

| Prop          | Type              | Description                                                                                                                          |
| ------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `children?`   | `React.ReactNode` |                                                                                                                                      |
| `delay?`      | `number`          | How long to wait before opening the tooltip on hover. Specified in milliseconds. Default: `0`                                        |
| `closeDelay?` | `number`          | How long to wait before closing a tooltip. Specified in milliseconds.                                                                |
| `timeout?`    | `number`          | Another tooltip will open instantly if the previous tooltip is closed within this timeout. Specified in milliseconds. Default: `400` |

### Tooltip

| Prop                     | Type                                                                       | Description                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------------ | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultOpen?`           | `boolean`                                                                  | Whether the tooltip is initially open. To render a controlled tooltip, use the `open` prop instead. Default: `false`                                                                                                                                                                                                                                                                |
| `open?`                  | `boolean`                                                                  | Whether the tooltip is currently open.                                                                                                                                                                                                                                                                                                                                              |
| `onOpenChange?`          | `((open: boolean, eventDetails: Tooltip.Root.ChangeEventDetails) => void)` | Event handler called when the tooltip is opened or closed.                                                                                                                                                                                                                                                                                                                          |
| `onOpenChangeComplete?`  | `((open: boolean) => void)`                                                | Event handler called after any animations complete when the tooltip is opened or closed.                                                                                                                                                                                                                                                                                            |
| `disableHoverablePopup?` | `boolean`                                                                  | Whether the tooltip contents can be hovered without closing the tooltip. Default: `false`                                                                                                                                                                                                                                                                                           |
| `trackCursorAxis?`       | `"both" \| "none" \| "x" \| "y"`                                           | Determines which axis the tooltip should track the cursor on. Default: `'none'`                                                                                                                                                                                                                                                                                                     |
| `actionsRef?`            | `React.RefObject<TooltipRootActions \| null>`                              | A ref to imperative actions. - `unmount`: Ends the closing phase of the tooltip after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onOpenChange` first, otherwise the tooltip completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the tooltip imperatively when called. |
| `disabled?`              | `boolean`                                                                  | Whether the tooltip is disabled. Default: `false`                                                                                                                                                                                                                                                                                                                                   |
| `handle?`                | `Tooltip.Handle<unknown>`                                                  | A handle to associate the tooltip with a trigger. If specified, allows external triggers to control the tooltip's open state. Can be created with the Tooltip.createHandle() method.                                                                                                                                                                                                |
| `children?`              | `PayloadChildRenderFunction<unknown> \| React.ReactNode`                   | The content of the tooltip. This can be a regular React node or a render function that receives the `payload` of the active trigger.                                                                                                                                                                                                                                                |
| `triggerId?`             | `string \| null`                                                           | ID of the trigger that the tooltip is associated with. This is useful in conjunction with the `open` prop to create a controlled tooltip. There's no need to specify this prop when the tooltip is uncontrolled (that is, when the `open` prop is not set).                                                                                                                         |
| `defaultTriggerId?`      | `string \| null`                                                           | ID of the trigger that the tooltip is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open tooltip.                                                                                                                                                                                                                               |

### TooltipTrigger

| Prop            | Type                      | Description                                                                                                                                                                                                                                                                                               |
| --------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `handle?`       | `Tooltip.Handle<unknown>` | A handle to associate the trigger with a tooltip.                                                                                                                                                                                                                                                         |
| `payload?`      | `unknown`                 | A payload to pass to the tooltip when it is opened.                                                                                                                                                                                                                                                       |
| `delay?`        | `number`                  | How long to wait before opening the tooltip on hover. Specified in milliseconds. Default: `600`                                                                                                                                                                                                           |
| `closeOnClick?` | `boolean`                 | Whether the tooltip should close when this trigger is clicked. Default: `true`                                                                                                                                                                                                                            |
| `closeDelay?`   | `number`                  | How long to wait before closing the tooltip. Specified in milliseconds. Default: `0`                                                                                                                                                                                                                      |
| `disabled?`     | `boolean`                 | If `true`, the tooltip will not open when interacting with this trigger. Note that this doesn't apply the `disabled` attribute to the trigger element. If you want to disable the trigger element itself, you can pass the `disabled` prop to the trigger element via the `render` prop. Default: `false` |
| `openOnTap?`    | `boolean`                 | Opens the tooltip when the trigger is tapped on a touch screen, where hovering can't open it. A second tap or a tap elsewhere closes it. Defaults to `true` on disabled triggers, since tapping them does nothing else.                                                                                   |

### TooltipContent

| Prop           | Type                                                                                                                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `anchor?`      | `Element \| React.RefObject<Element \| null> \| VirtualElement \| (() => Element \| VirtualElement \| null) \| null` | An element to position the popup against. By default, the popup will be positioned against the trigger.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `sideOffset?`  | `number \| OffsetFunction`                                                                                           | Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. Default: `6` |
| `align?`       | `"center" \| "end" \| "start"`                                                                                       | How to align the popup relative to the specified side. Default: `"center"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `alignOffset?` | `number \| OffsetFunction`                                                                                           | Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment. The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. Default: `0`  |
| `side?`        | `"bottom" \| "inline-end" \| "inline-start" \| "left" \| "right" \| "top"`                                           | Which side of the anchor element to align the popup against. May automatically change to avoid collisions. Default: `"top"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
