Tooltip
Overview
A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it. STooltip combines the Aria TooltipRoot/TooltipTrigger/TooltipPositioner/TooltipPopup/TooltipArrow primitive family (built on the shared Popper) with the tooltipVariants style recipe (3 slots, 6 sizes).
Use a tooltip for a short, non-interactive hint. For rich hover content use hover-card; for click-triggered content use popover; for a confirmation use popconfirm.
Usage
Features
- 🧩 Aria/styled split —
TooltipCompactaggregates the popper trigger, positioner, popup and arrow;STooltiponly injects styles and forwards slots/events - 🖱️ Hover + focus — opens on pointer hover or keyboard focus; dismisses on Escape or pointer leave
- ⏱️ Delay tuning —
delayDuration(open) andskipDelayDuration(between triggers), with global defaults viaConfigProvider - 🎯 Placement — full popper
placementcontrol with collision avoidance and side-aware slide animations - 🔽 Arrow —
showArrowrenders a positioned arrow; configurable viaarrowProps - 🏷️ Content —
contentprop or the default slot - ♿ Behavior toggles —
ignoreNonKeyboardFocus,disableHoverableContent,disableClosingTrigger,disabled - 📐 6 sizes — xs–2xl
size; per-slotuioverrides - ♿ Accessible —
role="tooltip"(visually-hidden text node), non-modal,axe-corezero violations open and closed
Component family
STooltip(styled) — the entry wrapper;tooltipVariantsrecipe with dynamic slot forwardingTooltipRoot(Aria) — the state owner;openviauseControllableState,delayDuration/skipDelayDuration/disabled/ignoreNonKeyboardFocus, merges the globalConfigProvidertooltip configTooltipTrigger(Aria) — the anchor that opens the tooltip on hover/focusTooltipPositioner/TooltipPositionerImpl(Aria) — the positioned surface (built onPopperPositioner)TooltipPopup(Aria) — the popup body; exposes a visually-hiddenrole="tooltip"text node for screen readersTooltipArrow(Aria) — the popper arrowTooltipCompact(Aria) — the aggregated composite; composes trigger/positioner/popup/arrow and exposes the slots
Demos
Basic
API
Aria Layer
9Tooltip
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | class of popup |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<TooltipUi> | — | Per-slot class overrides for the component. |
content | string | — | Content. |
placement | Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopperAnchorProps | — | Properties forwarded to the trigger element. |
portalProps | TooltipPortalProps | — | Properties forwarded to the portal element. |
positionerProps | TooltipPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | TooltipPopupProps | — | Properties forwarded to the popup element. |
arrowProps | TooltipArrowProps | — | Properties forwarded to the arrow element. |
defaultOpen | boolean | — | The open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the tooltip. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | When `true`, disable tooltip |
delayDuration | number | 150 | The duration from when the pointer enters the trigger until the tooltip gets opened. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableHoverableContent | boolean | false | When `true`, trying to hover the content will result in the tooltip closing as the pointer leaves the trigger. |
disableClosingTrigger | boolean | false | When `true`, clicking on trigger will not close the content. |
ignoreNonKeyboardFocus | boolean | false | Prevent the tooltip from opening if the focus did not come from the keyboard by matching against the
`:focus-visible` selector. This is useful if you want to avoid opening it when switching browser tabs or closing a
dialog. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the tooltip changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
placed | — | — |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
trigger | — | Custom content for the trigger slot. |
TooltipArrow
TooltipCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
content | string | — | Content. |
placement | Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopperAnchorProps | — | Properties forwarded to the trigger element. |
portalProps | TooltipPortalProps | — | Properties forwarded to the portal element. |
positionerProps | TooltipPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | TooltipPopupProps | — | Properties forwarded to the popup element. |
arrowProps | TooltipArrowProps | — | Properties forwarded to the arrow element. |
defaultOpen | boolean | — | The open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the tooltip. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | When `true`, disable tooltip |
delayDuration | number | 150 | The duration from when the pointer enters the trigger until the tooltip gets opened. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableHoverableContent | boolean | false | When `true`, trying to hover the content will result in the tooltip closing as the pointer leaves the trigger. |
disableClosingTrigger | boolean | false | When `true`, clicking on trigger will not close the content. |
ignoreNonKeyboardFocus | boolean | false | Prevent the tooltip from opening if the focus did not come from the keyboard by matching against the
`:focus-visible` selector. This is useful if you want to avoid opening it when switching browser tabs or closing a
dialog. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the tooltip changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
placed | — | — |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
trigger | — | Custom content for the trigger slot. |
TooltipPopup
Props
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | By default, screen readers will announce the content inside the component.
If this is not descriptive enough, or you have content that cannot be announced, use `aria-label` as a more
descriptive label. |
TooltipPortal
TooltipPositioner
Props
| Prop | Type | Default | Description |
|---|---|---|---|
placement | Placement | undefined | The placement of the floating element.
If used, it will override the `side` and `align` props. |
side | Side | 'bottom' | The preferred side of the trigger to render against when open. Will be reversed when collisions occur and
avoidCollisions is enabled. |
sideOffset | number | 0 | The distance in pixels from the trigger. |
sideFlip | boolean | true | Flip to the opposite side when colliding with boundary. |
align | Align | 'center' | The preferred alignment against the trigger. May change when collisions occur. |
alignOffset | number | 0 | An offset in pixels from the `start` or `end` alignment options. |
alignFlip | boolean | true | Flip alignment when colliding with boundary.
May only occur when `prioritizePosition` is true. |
avoidCollisions | boolean | true | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
collisionBoundary | Element | (Element | null)[] | null | [ ] | The element used as the collision boundary. By default this is the viewport, though you can provide additional
element(s) to be included in this check. |
collisionPadding | Padding | 0 | The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for
all sides), or a partial padding object, for example: { top: 20, left: 20 }. |
arrowPadding | number | 0 | The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it
from overflowing the corners. |
hideShiftedArrow | boolean | true | When `true`, hides the arrow when it cannot be centered
to the reference element. |
sticky | 'partial' | 'always' | 'partial' | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at
least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
hideWhenDetached | boolean | false | Whether to hide the content when the trigger becomes fully occluded. |
positionStrategy | 'fixed' | 'absolute' | 'fixed' | The type of CSS position property to use. |
updatePositionStrategy | 'always' | 'optimized' | 'optimized' | Strategy to update the position of the floating element on every animation frame. |
disableUpdateOnLayoutShift | boolean | false | Whether to disable the update position for the content when the layout shifted. |
prioritizePosition | boolean | false | Force content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElement | — | The custom element or virtual element that will be set as the reference to position the floating element. If
provided, it will replace the default anchor element. |
trapFocus | boolean | modal | Whether focus is trapped inside the popup while open (Tab cycles within the layer and body
scroll is locked for modal layers). |
disableHoverableContent | boolean | false | When `true`, hovering the popup closes instead of keeping it open (the grace area is disabled,
so leaving the trigger closes the layer immediately). |
onGracePointerExit | (() => void) | — | Callback invoked when the pointer finally leaves the grace area. Lets an upper layer (e.g.
Tooltip) run area-scoped close logic without registering its own second grace area. |
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
placed | — | — |
TooltipProvider
Props
| Prop | Type | Default | Description |
|---|---|---|---|
delayDuration | number | 150 | The duration from when the pointer enters the trigger until the tooltip gets opened. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableHoverableContent | boolean | false | When `true`, trying to hover the content will result in the tooltip closing as the pointer leaves the trigger. |
disableClosingTrigger | boolean | false | When `true`, clicking on trigger will not close the content. |
disabled | boolean | false | When `true`, disable tooltip |
ignoreNonKeyboardFocus | boolean | false | Prevent the tooltip from opening if the focus did not come from the keyboard by matching against the
`:focus-visible` selector. This is useful if you want to avoid opening it when switching browser tabs or closing a
dialog. |
positionerProps | TooltipPositionerProps | — | Props to be passed down to the positioner. Useful when you need to control the positioner, such as disabling it when the trigger is disabled. |
TooltipRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | — | The open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the tooltip. |
dir | Direction | — | — |
modal | boolean | — | — |
disabled | boolean | — | When `true`, disable tooltip |
delayDuration | number | 150 | The duration from when the pointer enters the trigger until the tooltip gets opened. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableHoverableContent | boolean | false | When `true`, trying to hover the content will result in the tooltip closing as the pointer leaves the trigger. |
disableClosingTrigger | boolean | false | When `true`, clicking on trigger will not close the content. |
ignoreNonKeyboardFocus | boolean | false | Prevent the tooltip from opening if the focus did not come from the keyboard by matching against the
`:focus-visible` selector. This is useful if you want to avoid opening it when switching browser tabs or closing a
dialog. |
positionerProps | TooltipPositionerProps | — | Props to be passed down to the positioner. Useful when you need to control the positioner, such as disabling it when the trigger is disabled. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean, reason?: PopperOpenChangeReason | undefined | Event handler called when the open state of the tooltip changes. |
TooltipTrigger
Notes
Architecture and benchmark differences
TooltipCompact owns the trigger/positioner/popup/arrow composition while every primitive stays style-free and only the UI wrapper injects the tooltipVariants classes. This mirrors radix-ui/shadcn-ui’s headless split, built on the shared Popper primitives. Ant Design, Element Plus, Mantine and Naive UI ship a single styled tooltip with title/placement props; VeanUI additionally exposes per-slot *Props, a size scale, an arrow toggle, a delayDuration/skipDelayDuration tuning model with ConfigProvider global defaults, and an accessible visually-hidden role="tooltip" node.
| Capability | VeanUI | shadcn/ui | Ant Design Tooltip | Element Plus Tooltip | Mantine Tooltip | Naive UI Tooltip |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| Popper placement (12) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Delay tuning | ✅ | ✅ | — | ✅ | ✅ | — |
| Arrow | ✅ | ✅ | — | — | — | — |
| Sizes (6) | ✅ | — | — | — | — | — |
| Focus trigger | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
— = unsupported or a different interaction model.
Cautions
- The tooltip opens on hover and keyboard focus; tune
delayDuration/skipDelayDuration(or theConfigProvidertooltip defaults) to reduce accidental triggers. ignoreNonKeyboardFocus(default) ignores non-keyboard focus (e.g. programmatic), matching native tooltip semantics.- The popup is rendered through a
Portal(teleported todocument.body); position it inside a relatively-positioned ancestor only withportalProps: { disabled: true }. showArrowrenders an arrow;arrowPropscan override its position/classes.- The tooltip is non-modal and does not trap focus; it dismisses on Escape or pointer leave.
Roadmap
N/A — tooltip is feature-complete for the current parity set.
FAQ
How do I add a tooltip to an element?
Wrap the element in the trigger slot and set content:
<STooltip content="Delete this item">
<template #trigger><SButton>Delete</SButton></template>
</STooltip>How do I position the tooltip?
Use placement:
<STooltip placement="top" content="Copy">
<template #trigger><SButton>Copy</SButton></template>
</STooltip>How do I adjust the open timing?
Set delay-duration (ms), with skip-delay-duration between triggers:
<STooltip :delay-duration="150" :skip-delay-duration="50" content="Save">
<template #trigger><SButton>Save</SButton></template>
</STooltip>How do I hide the arrow?
Set show-arrow={false}:
<STooltip :show-arrow="false" content="Info">
<template #trigger><SButton>?</SButton></template>
</STooltip>