---
title: Tooltip
description: A popup that displays contextual information when an element is hovered or focused.
order: 35
tags: [components]
---

import { Preview } from "$lib/components/preview";
import { Button, Icon, Tooltip } from "areia";
import { Info, Languages, Plus, Settings } from "lucide";

# Tooltip

A popup that displays contextual information when an element is hovered or focused.

[Source Code](https://github.com/ilhajs/areia/blob/main/packages/areia/src/components/tooltip/index.ts)

Areia's `Tooltip` is built on `@data-slot/tooltip` and rendered as an Ilha island when you use `Tooltip`. It handles hover/focus interactions, warm-up behavior between nearby tooltips, collision-aware positioning, Escape dismissal, and accessible tooltip attributes.

<Preview
  code={
    'import ilha from "ilha";\nimport { Plus } from "lucide";\nimport { Button, Icon, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip content="Create project">\n    <Button\n      shape="square"\n      icon={<Icon icon={Plus} />}\n      aria-label="Create project"\n    />\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip content="Create project">
    <Button
      shape="square"
      icon={<Icon icon={Plus} />}
      aria-label="Create project"
    />
  </Tooltip>
</Preview>

## Import

```ts
import { Tooltip } from "areia";
```

## Usage

Call `Tooltip(...)` for an interactive island. The root renders the tooltip markup and initializes the `@data-slot/tooltip` controller after mount.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip content="Helpful contextual information">\n    <Button>Hover me</Button>\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip content="Helpful contextual information">
    <Button>Hover me</Button>
  </Tooltip>
</Preview>

For static server-rendered markup or custom initialization, use `Tooltip.Static(...)`. For most application usage, call `Tooltip(...)`.

## Examples

### Basic Tooltip

Use `content` for the popup and `children` for the trigger content.

<Preview
  code={
    'import ilha from "ilha";\nimport { Info } from "lucide";\nimport { Button, Icon, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip content="This action cannot be undone.">\n    <Button\n      variant="secondary"\n      shape="square"\n      icon={<Icon icon={Info} />}\n      aria-label="More information"\n    />\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip content="This action cannot be undone.">
    <Button
      variant="secondary"
      shape="square"
      icon={<Icon icon={Info} />}
      aria-label="More information"
    />
  </Tooltip>
</Preview>

### Text Trigger

When the trigger is not an action, render it as a `span`.

<Preview
  code={
    'import ilha from "ilha";\nimport { Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip\n    triggerAs="span"\n    triggerClass="underline decoration-dotted underline-offset-4"\n    content="Areia is a vanilla TypeScript component library."\n  >\n    Areia\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip
    triggerAs="span"
    triggerClass="underline decoration-dotted underline-offset-4"
    content="Areia is a vanilla TypeScript component library."
  >
    Areia
  </Tooltip>
</Preview>

### Multiple Tooltips

Tooltips share `@data-slot/tooltip` warm-up behavior automatically. After one tooltip closes, hovering another tooltip within `skipDelayDuration` opens it without the normal delay.

<Preview
  code={
    'import ilha from "ilha";\nimport { Languages, Plus, Settings } from "lucide";\nimport { Button, Icon, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex items-center gap-2">\n    <Tooltip content="Create project">\n      <Button\n        shape="square"\n        icon={<Icon icon={Plus} />}\n        aria-label="Create project"\n      />\n    </Tooltip>\n    <Tooltip content="Translate">\n      <Button\n        shape="square"\n        icon={<Icon icon={Languages} />}\n        aria-label="Translate"\n      />\n    </Tooltip>\n    <Tooltip content="Settings">\n      <Button\n        shape="square"\n        icon={<Icon icon={Settings} />}\n        aria-label="Settings"\n      />\n    </Tooltip>\n  </div>\n));'
  }
  lang="tsx"
>
  <div class="flex items-center gap-2">
    <Tooltip content="Create project">
      <Button
        shape="square"
        icon={<Icon icon={Plus} />}
        aria-label="Create project"
      />
    </Tooltip>
    <Tooltip content="Translate">
      <Button
        shape="square"
        icon={<Icon icon={Languages} />}
        aria-label="Translate"
      />
    </Tooltip>
    <Tooltip content="Settings">
      <Button
        shape="square"
        icon={<Icon icon={Settings} />}
        aria-label="Settings"
      />
    </Tooltip>
  </div>
</Preview>

### Side

Use `side` to choose the preferred side of the trigger. Collision handling may flip the tooltip at runtime when there is not enough space.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <div class="grid grid-cols-2 gap-3">\n    <Tooltip side="top" content="Top tooltip">\n      <Button>Top</Button>\n    </Tooltip>\n    <Tooltip side="bottom" content="Bottom tooltip">\n      <Button>Bottom</Button>\n    </Tooltip>\n    <Tooltip side="left" content="Left tooltip">\n      <Button>Left</Button>\n    </Tooltip>\n    <Tooltip side="right" content="Right tooltip">\n      <Button>Right</Button>\n    </Tooltip>\n  </div>\n));'
  }
  lang="tsx"
>
  <div class="grid grid-cols-2 gap-3">
    <Tooltip side="top" content="Top tooltip">
      <Button>Top</Button>
    </Tooltip>
    <Tooltip side="bottom" content="Bottom tooltip">
      <Button>Bottom</Button>
    </Tooltip>
    <Tooltip side="left" content="Left tooltip">
      <Button>Left</Button>
    </Tooltip>
    <Tooltip side="right" content="Right tooltip">
      <Button>Right</Button>
    </Tooltip>
  </div>
</Preview>

### Alignment

Use `align` to align the tooltip along the selected side.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex items-center gap-3">\n    <Tooltip align="start" content="Start aligned">\n      <Button>Start</Button>\n    </Tooltip>\n    <Tooltip align="center" content="Center aligned">\n      <Button>Center</Button>\n    </Tooltip>\n    <Tooltip align="end" content="End aligned">\n      <Button>End</Button>\n    </Tooltip>\n  </div>\n));'
  }
  lang="tsx"
>
  <div class="flex items-center gap-3">
    <Tooltip align="start" content="Start aligned">
      <Button>Start</Button>
    </Tooltip>
    <Tooltip align="center" content="Center aligned">
      <Button>Center</Button>
    </Tooltip>
    <Tooltip align="end" content="End aligned">
      <Button>End</Button>
    </Tooltip>
  </div>
</Preview>

### Delay Control

Use `delay` to control how long to wait before opening. Use `skipDelayDuration` to control the warm-up window after a tooltip closes.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <div class="flex flex-wrap items-center gap-3">\n    <Tooltip delay={1000} content="Opens after one second">\n      <Button>1s delay</Button>\n    </Tooltip>\n    <Tooltip\n      delay={0}\n      skipDelayDuration={0}\n      content="Opens instantly with no warm-up window"\n    >\n      <Button>Instant</Button>\n    </Tooltip>\n  </div>\n));'
  }
  lang="tsx"
>
  <div class="flex flex-wrap items-center gap-3">
    <Tooltip delay={1000} content="Opens after one second">
      <Button>1s delay</Button>
    </Tooltip>
    <Tooltip
      delay={0}
      skipDelayDuration={0}
      content="Opens instantly with no warm-up window"
    >
      <Button>Instant</Button>
    </Tooltip>
  </div>
</Preview>

### Without Arrow

Set `arrow={false}` to hide the decorative arrow.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip arrow={false} content="No arrow">\n    <Button>Hover me</Button>\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip arrow={false} content="No arrow">
    <Button>Hover me</Button>
  </Tooltip>
</Preview>

### Custom Content

The `content` prop accepts markup.

<Preview
  code={
    'import ilha from "ilha";\nimport { Button, Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip\n    content={\n      <div class="flex max-w-48 flex-col gap-1">\n        <span class="font-medium">Deploy preview</span>\n        <span class="text-areia-subtle">\n          Creates a temporary preview environment.\n        </span>\n      </div>\n    }\n  >\n    <Button>Deploy</Button>\n  </Tooltip>\n));'
  }
  lang="tsx"
>
  <Tooltip
    content={
      <div class="flex max-w-48 flex-col gap-1">
        <span class="font-medium">Deploy preview</span>
        <span class="text-areia-subtle">
          Creates a temporary preview environment.
        </span>
      </div>
    }
  >
    <Button>Deploy</Button>
  </Tooltip>
</Preview>

### Custom Trigger

Use `trigger` when you need complete control over the trigger markup. The custom trigger must include `data-slot="tooltip-trigger"`.

<Preview
  code={
    'import ilha from "ilha";\nimport { Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip\n    content="Custom trigger markup"\n    trigger={\n      <span\n        data-slot="tooltip-trigger"\n        tabindex="0"\n        class="inline-flex rounded-full bg-areia-surface-muted px-2 py-1 text-sm text-areia-default"\n      >\n        Beta\n      </span>\n    }\n  />\n));'
  }
  lang="tsx"
  codeOnly
/>

### Static Composition

Use low-level parts for static markup or when you want to call `createTooltip` yourself.

<Preview
  code={
    'import ilha from "ilha";\nimport { Tooltip } from "areia";\n\nexport default ilha.render(() => (\n  <Tooltip.Static\n    content="Static tooltip markup"\n    triggerAs="span"\n  >\n    Hover me\n  </Tooltip.Static>\n));'
  }
  lang="tsx"
>
  <Tooltip.Static
    content="Static tooltip markup"
    triggerAs="span"
  >
    Hover me
  </Tooltip.Static>
</Preview>

## API Reference

### Tooltip

`Tooltip` is an Ilha island. It accepts standard HTML `div` attributes plus `@data-slot/tooltip` options.

| Prop                | Type                                                                       | Default    | Description                                                                                                 |
| ------------------- | -------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------- |
| `content`           | `unknown`                                                                  | -          | Content to display inside the tooltip popup.                                                                |
| `children`          | `unknown`                                                                  | -          | Trigger content when `trigger` is not provided.                                                             |
| `trigger`           | `unknown`                                                                  | -          | Custom trigger markup. Must include `data-slot="tooltip-trigger"`.                                          |
| `triggerAs`         | `"button" \| "span" \| "div" \| "a"`                                       | `"span"`   | Tag used for the generated trigger. Defaults to `span` to avoid nested buttons when wrapping `Button(...)`. |
| `arrow`             | `boolean`                                                                  | `true`     | Whether to render the decorative arrow.                                                                     |
| `class`             | `string`                                                                   | -          | Additional CSS classes applied to the tooltip root.                                                         |
| `className`         | `string`                                                                   | -          | Alias for `class`.                                                                                          |
| `triggerClass`      | `string`                                                                   | -          | Additional CSS classes applied to the generated trigger.                                                    |
| `triggerClassName`  | `string`                                                                   | -          | Alias for `triggerClass`.                                                                                   |
| `contentClass`      | `string`                                                                   | -          | Additional CSS classes applied to the tooltip popup.                                                        |
| `contentClassName`  | `string`                                                                   | -          | Alias for `contentClass`.                                                                                   |
| `side`              | `"top" \| "right" \| "bottom" \| "left" \| "inline-start" \| "inline-end"` | `"top"`    | Preferred side relative to the trigger.                                                                     |
| `align`             | `"start" \| "center" \| "end"`                                             | `"center"` | Preferred alignment along the selected side.                                                                |
| `sideOffset`        | `number`                                                                   | `4`        | Distance from the trigger in pixels.                                                                        |
| `alignOffset`       | `number`                                                                   | `0`        | Offset from the aligned edge in pixels.                                                                     |
| `avoidCollisions`   | `boolean`                                                                  | `true`     | Flip or shift to stay inside the viewport.                                                                  |
| `collisionPadding`  | `number`                                                                   | `8`        | Viewport edge padding used for collision handling.                                                          |
| `delay`             | `number`                                                                   | `300`      | Delay before showing the tooltip in milliseconds.                                                           |
| `skipDelayDuration` | `number`                                                                   | `300`      | Warm-up window where another tooltip opens without delay. Set `0` to disable.                               |
| `portal`            | `boolean`                                                                  | `true`     | Portal content to `document.body` while open.                                                               |
| `onOpenChange`      | `(open: boolean) => void`                                                  | -          | Called when visibility changes.                                                                             |

### Tooltip.Trigger

Generated trigger element.

| Prop        | Type                                 | Default  | Description             |
| ----------- | ------------------------------------ | -------- | ----------------------- |
| `as`        | `"button" \| "span" \| "div" \| "a"` | `"span"` | Trigger tag name.       |
| `children`  | `unknown`                            | -        | Trigger content.        |
| `class`     | `string`                             | -        | Additional CSS classes. |
| `className` | `string`                             | -        | Alias for `class`.      |

### Tooltip.Content

Popup container.

| Prop        | Type                                                                       | Default | Description                        |
| ----------- | -------------------------------------------------------------------------- | ------- | ---------------------------------- |
| `children`  | `unknown`                                                                  | -       | Tooltip content.                   |
| `arrow`     | `boolean`                                                                  | `true`  | Whether to render `Tooltip.Arrow`. |
| `side`      | `"top" \| "right" \| "bottom" \| "left" \| "inline-start" \| "inline-end"` | `"top"` | Preferred side styling.            |
| `class`     | `string`                                                                   | -       | Additional CSS classes.            |
| `className` | `string`                                                                   | -       | Alias for `class`.                 |

### Tooltip.Arrow

Decorative arrow element. The controller positions it with inline `top`/`left` values and `data-side` attributes.

| Prop        | Type      | Default | Description             |
| ----------- | --------- | ------- | ----------------------- |
| `children`  | `unknown` | SVG     | Custom arrow content.   |
| `class`     | `string`  | -       | Additional CSS classes. |
| `className` | `string`  | -       | Alias for `class`.      |

## Accessibility

### Behavior

- Opens on pointer hover for mouse/pen input.
- Opens on keyboard focus.
- Touch hover is ignored; touch devices use focus behavior.
- Pressing `Escape` hides the tooltip.
- Moving from the trigger to the tooltip content keeps it open.
- Disabled triggers do not open tooltips.

### ARIA

The controller automatically handles:

- `role="tooltip"` on the popup content.
- `aria-describedby` on the trigger while the tooltip is open.
- `aria-hidden="true"` / `"false"` on the tooltip content.
- Stable generated IDs for content when needed.

### Trigger Labels

Tooltips are supplemental. Icon-only triggers still need an accessible name, usually with `aria-label` on the trigger button.
