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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | The trigger element the tooltip attaches to. |
| content | ReactNode | — | Tooltip content. |
children
ReactNode
default: —
The trigger element the tooltip attaches to.
content
ReactNode
default: —
Tooltip content.
Appearance
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "xs" | "xs-sm" | "sm" | "md" | "lg" | "xl" | md | Tooltip size. |
| color | "default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger" | default | Semantic color. |
| colorDepth | number | 100 | Shade depth for the selected color. |
| radius | "none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | "full" | md | Border radius. |
| shadow | "none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | sm | Box-shadow depth. |
| showArrow | boolean | false | Shows a pointing arrow. |
| triggerScaleOnOpen | boolean | false | Slightly scales the trigger while the tooltip is open. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | "top" | "bottom" | "left" | "right" | ... (12-way) | top | Position relative to the trigger. |
| offset | number | 7 | Distance from the trigger. |
| containerPadding | number | 12 | Minimum distance kept from the viewport edge. |
| crossOffset | number | 0 | Offset along the cross axis. |
| shouldFlip | boolean | true | Flips placement to stay in the viewport. |
| portalContainer | Element | DocumentFragment | null | — | Custom portal mount target. |
| updatePositionDeps | unknown[] | — | Extra dependencies that make the tooltip re-measure its position, for example when its content changes size while open. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| delay | number | 0 | Open delay in ms. |
| closeDelay | number | 500 | Close delay in ms (also floors touch-triggered opens). |
| isOpen | boolean | — | Controlled open state. |
| defaultOpen | boolean | false | Uncontrolled initial open state. |
| isKeyboardDismissDisabled | boolean | false | Disables closing via Escape. |
| isDismissable | boolean | false | Allows dismissing an isOpen-controlled tooltip via outside click/Escape. |
| shouldCloseOnBlur | boolean | true | Closes when the trigger loses focus. |
| shouldCloseOnInteractOutside | (element: HTMLElement) => boolean | — | Custom predicate for outside-interaction dismissal. |
| isDisabled | boolean | false | Disables the tooltip entirely. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| disableAnimation | boolean | false | Disables the enter/exit animation. |
disableAnimation
boolean
default: false
Disables the enter/exit animation.
Events
| Prop | Type | Default | Description |
|---|---|---|---|
| onOpenChange | (isOpen: boolean) => void | — | Called when open state changes. |
| onClose | () => void | — | Called when the tooltip closes. |
onOpenChange
(isOpen: boolean) => void
default: —
Called when open state changes.
onClose
() => void
default: —
Called when the tooltip closes.
Styling
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Class applied to the tooltip content. |
| classNames | { base?; content?; arrow? } | — | Per-slot class overrides. |
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.