Skip to content

Tooltip component

Tooltip wraps a single trigger element and shows contextual content on hover or focus — a label for an icon-only button, a hint for a form field, extra detail on truncated text. It positions itself relative to the trigger with automatic flipping to stay in the viewport, and its hover/touch timing adapts per-interaction rather than assuming one input method for the whole session.

Data Display30 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Tooltip actually gets used — each one a real, self-contained example.

Label an icon button

The classic use: a name for an icon-only button. Tooltip opens on hover or keyboard focus, so it's reachable either way — wrap a real focusable element.

<Tooltip content="Copy to clipboard">
  <Button isIconOnly variant="ghost" aria-label="Copy">
    <CopyIcon />
  </Button>
</Tooltip>

Placement and arrow

placement positions the tooltip (12-way, with auto-flip near edges); showArrow adds a pointer connecting it to the trigger.

<Tooltip content="Delete" placement="bottom" showArrow>
  <Button isIconOnly color="danger" variant="ghost" aria-label="Delete">
    <TrashIcon />
  </Button>
</Tooltip>

Semantic colors

color tints the tooltip surface — useful to echo the tone of the action it describes.

<Tooltip content="Looks good" color="success">
  <Button variant="bordered">Success</Button>
</Tooltip>
<Tooltip content="Careful" color="warning">
  <Button variant="bordered">Warning</Button>
</Tooltip>

Hover delay

A small delay (150–300ms) keeps tooltips on frequently-hovered UI from firing on every incidental mouse pass; closeDelay holds it open briefly after leave.

<Tooltip content="Advanced settings" delay={300} closeDelay={100}>
  <Button variant="ghost">Settings</Button>
</Tooltip>

Basic usage

Tooltip takes exactly one child — the trigger — and content for what to show. It opens on hover or keyboard focus and closes on the corresponding leave/blur, so a keyboard-only user reaches it the same way a mouse user does.

<Tooltip content="Copy to clipboard">
  <Button isIconOnly variant="ghost" aria-label="Copy">
    <CopyIcon />
  </Button>
</Tooltip>

Placement

placement supports the full 12-way positioning set (each side, plus start/end variants), with shouldFlip on by default so it automatically switches sides rather than rendering off-screen near a viewport edge. showArrow adds a small pointer connecting the tooltip back to its trigger.

<Tooltip content="Delete" placement="bottom" showArrow>
  <Button isIconOnly color="danger" variant="ghost" aria-label="Delete">
    <TrashIcon />
  </Button>
</Tooltip>

Delay and touch behavior

delay controls how long a hover has to hold before the tooltip opens (0 by default — instant); closeDelay controls how long it stays open after the pointer leaves, and also acts as a floor for touch-triggered opens so a tap-and-release on a touch device doesn't flash the tooltip for an imperceptible instant. This is decided per interaction event, not a single device check, so a device with both touch and mouse input (a touchscreen laptop) gets correct behavior for whichever input actually triggered it.

<Tooltip content="Advanced settings" delay={300} closeDelay={100}>
  <Button variant="ghost">Settings</Button>
</Tooltip>

Controlled visibility

For a tooltip driven by something other than hover/focus (showing it programmatically after an action, keeping it open during a guided tour step), use isOpen + onOpenChange instead of the default uncontrolled hover behavior.

<Tooltip content="Click here to continue" isOpen={tourStep === 2} onOpenChange={setTourVisible}>
  <Button color="primary">Next</Button>
</Tooltip>

Props

The full public API for Tooltip. Every prop is stable and intentionally typed.

Content

children

ReactNode

default: —

The trigger element the tooltip attaches to.

content

ReactNode

default: —

Tooltip content.

Appearance

size

"xs" | "xs-sm" | "sm" | "md" | "lg" | "xl"

default: md

Tooltip size.

color

"default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger"

default: default

Semantic color.

colorDepth

number

default: 100

Shade depth for the selected color.

radius

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | "full"

default: md

Border radius.

shadow

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg"

default: sm

Box-shadow depth.

showArrow

boolean

default: false

Shows a pointing arrow.

triggerScaleOnOpen

boolean

default: false

Slightly scales the trigger while the tooltip is open.

Layout & positioning

placement

"top" | "bottom" | "left" | "right" | ... (12-way)

default: top

Position relative to the trigger.

offset

number

default: 7

Distance from the trigger.

containerPadding

number

default: 12

Minimum distance kept from the viewport edge.

crossOffset

number

default: 0

Offset along the cross axis.

shouldFlip

boolean

default: true

Flips placement to stay in the viewport.

portalContainer

Element | DocumentFragment | null

default: —

Custom portal mount target.

updatePositionDeps

unknown[]

default: —

Extra dependencies that make the tooltip re-measure its position, for example when its content changes size while open.

Behavior & states

delay

number

default: 0

Open delay in ms.

closeDelay

number

default: 500

Close delay in ms (also floors touch-triggered opens).

isOpen

boolean

default: —

Controlled open state.

defaultOpen

boolean

default: false

Uncontrolled initial open state.

isKeyboardDismissDisabled

boolean

default: false

Disables closing via Escape.

isDismissable

boolean

default: false

Allows dismissing an isOpen-controlled tooltip via outside click/Escape.

shouldCloseOnBlur

boolean

default: true

Closes when the trigger loses focus.

shouldCloseOnInteractOutside

(element: HTMLElement) => boolean

default: —

Custom predicate for outside-interaction dismissal.

isDisabled

boolean

default: false

Disables the tooltip entirely.

Motion

disableAnimation

boolean

default: false

Disables the enter/exit animation.

Events

onOpenChange

(isOpen: boolean) => void

default: —

Called when open state changes.

onClose

() => void

default: —

Called when the tooltip closes.

Styling

className

string

default: —

Class applied to the tooltip content.

classNames

{ base?; content?; arrow? }

default: —

Per-slot class overrides.

Accessibility

  • The trigger must be a real focusable element (a button, a link, an input) — a tooltip attached to a plain, non-interactive text node is unreachable by keyboard, since there's nothing to focus to trigger it.
  • shouldCloseOnBlur (on by default) closes the tooltip the moment its trigger loses focus, so it never lingers open after keyboard focus has moved elsewhere on the page.
  • isKeyboardDismissDisabled turns off Escape-to-close — leave it on (the default) for the vast majority of tooltips; only disable it for a tooltip that's meant to persist regardless of Escape.
  • Tooltip content should be supplementary, not the only place critical information lives — anything a user genuinely needs shouldn't be hidden behind a hover-only interaction.

Best practices

  • Always wrap a real interactive element (Button, a link, a native input) — never plain text or a decorative <span> — so the tooltip is reachable by keyboard, not just mouse hover.
  • Keep tooltip content short — a sentence at most. Anything longer belongs in a Popover-style pattern with its own dismiss affordance, not a hover tooltip.
  • Use a small delay (150-300ms) rather than 0 for tooltips on frequently-hovered UI (a toolbar of icon buttons) — instant tooltips firing on every incidental mouse pass become noisy rather than helpful.
  • Reserve controlled isOpen for cases that genuinely need it (guided tours, programmatic hints) — for ordinary hover/focus tooltips, the built-in uncontrolled behavior already handles delay, dismissal, and touch correctly.