Tooltip
Displays informative text when users hover over or focus on an element
Usage
import { Tooltip } from '@hareui/vue'<script setup lang="ts">import { Button, Tooltip } from '@hareui/vue'import { Icon } from '@iconify/vue'</script><template> <div class="flex items-center gap-4"> <Tooltip :delay="0" text="This is a tooltip"> <Button variant="secondary">Hover me</Button> </Tooltip> <Tooltip :delay="0" text="More information"> <Button aria-label="More information" icon-only variant="tertiary"> <Icon icon="gravity-ui:circle-info" /> </Button> </Tooltip> </div></template>Anatomy
Each Tooltip instance owns its own Reka TooltipProvider, so no app-level provider is needed. The first child is the trigger (as-child, merged directly onto its own root element — no extra wrapper is rendered); text or the #content slot supplies the floating content.
<Tooltip text="Helpful information about this element">
<Button>Hover for tooltip</Button>
</Tooltip>
<!-- or, with the content slot and an arrow -->
<Tooltip arrow>
<template #default><Button>Hover for tooltip</Button></template>
<template #content>Helpful information about this element</template>
</Tooltip>ui keys: base, arrow.
Examples
Placement
<script setup lang="ts">import { Button, Tooltip } from '@hareui/vue'</script><template> <div class="grid grid-cols-3 gap-4"> <div /> <Tooltip arrow :delay="0" side="top" text="Top placement"> <Button class="w-full" variant="tertiary">Top</Button> </Tooltip> <div /> <Tooltip arrow :delay="0" side="left" text="Left placement"> <Button class="w-full" variant="tertiary">Left</Button> </Tooltip> <div class="flex items-center justify-center"> <span class="text-sm text-muted">Hover buttons</span> </div> <Tooltip arrow :delay="0" side="right" text="Right placement"> <Button class="w-full" variant="tertiary">Right</Button> </Tooltip> <div /> <Tooltip arrow :delay="0" side="bottom" text="Bottom placement"> <Button class="w-full" variant="tertiary">Bottom</Button> </Tooltip> <div /> </div></template>With Arrow
<script setup lang="ts">import { Button, Tooltip } from '@hareui/vue'</script><template> <div class="flex items-center gap-4"> <Tooltip arrow :delay="0" text="Tooltip with arrow indicator"> <Button variant="secondary">With Arrow</Button> </Tooltip> <Tooltip arrow :delay="0" :side-offset="12" text="Custom offset from trigger"> <Button variant="primary">Custom Offset</Button> </Tooltip> </div></template>Custom Triggers
The default slot's root element becomes the trigger. To use non-focusable content (an Avatar, a Chip, an icon), wrap it in an inline-block element with role="button" and tabindex="0" (the equivalent of HeroUI's Tooltip.Trigger) so the tooltip stays reachable from the keyboard.
<script setup lang="ts">import { Avatar, Chip, Tooltip } from '@hareui/vue'import { Icon } from '@iconify/vue'// Equivalent of HeroUI's `Tooltip.Trigger`: a focusable inline-block wrapper around non-button content.const triggerClass = 'inline-block outline-none focus-visible:status-focused'</script><template> <div class="flex items-center gap-6"> <Tooltip arrow :delay="0"> <template #default> <div aria-label="User avatar" :class="triggerClass" role="button" tabindex="0"> <Avatar alt="Jane Doe" fallback="JD" size="sm" src="https://img.heroui.chat/image/avatar?w=400&h=400&u=4" /> </div> </template> <template #content> <div class="flex flex-col gap-0 py-1"> <p class="font-semibold">Jane Doe</p> <p class="text-xs text-muted">[email protected]</p> </div> </template> </Tooltip> <Tooltip class="flex items-center gap-1.5" :delay="0"> <template #default> <div aria-label="Status chip" :class="triggerClass" role="button" tabindex="0"> <Chip color="success"> <template #leading> <!-- @gravity-ui/icons with only `width` keeps its default height="16" --> <Icon height="16" icon="gravity-ui:circle-check-fill" width="12" /> </template> <template #default>Active</template> </Chip> </div> </template> <template #content> <span class="relative flex size-2"> <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75" /> <span class="relative inline-flex size-2 rounded-full bg-success" /> </span> <p>Jane is currently online</p> </template> </Tooltip> <Tooltip arrow :delay="0"> <template #default> <div aria-label="Info icon" :class="triggerClass" role="button" tabindex="0"> <div class="rounded-full bg-accent-soft p-2"> <Icon class="text-accent-soft-foreground" icon="gravity-ui:circle-question" /> </div> </div> </template> <template #content> <div class="max-w-xs px-1 py-1.5"> <p class="mb-1 font-semibold">Help Information</p> <p class="text-sm text-muted"> This is a helpful tooltip with more detailed information about this feature. </p> </div> </template> </Tooltip> </div></template>Delays and Focus-Only Trigger
delay (default 1500) and close-delay (default 500) match HeroUI's --tooltip-delay / --tooltip-close-delay defaults. The close delay applies only when the pointer leaves the trigger or tooltip; moving back in cancels it. Blur, press, Escape and another tooltip opening close it immediately. trigger="focus" opens the tooltip only when the trigger gets focus, and hovering neither opens nor closes it.
<Tooltip :delay="0" :close-delay="1000" text="Stays open for a second">
<Button variant="secondary">Close delay</Button>
</Tooltip>
<Tooltip trigger="focus" text="Shown on focus only">
<Button variant="secondary">Focus me</Button>
</Tooltip>Known differences from HeroUI
- No cross-tooltip warm-up. React Aria shares one global timer, so once any tooltip has opened, the next one opens instantly. Each HareUI Tooltip has its own Reka provider, so every tooltip waits for its own
delay. - Sub-pixel position drift. Reka positions the content with floating-ui and React Aria uses its own overlay maths. The two can differ by under a pixel.
- Delays are props, not CSS variables. HeroUI reads its defaults from
--tooltip-delay/--tooltip-close-delayat runtime. HareUI uses the same values as prop defaults, so changing those variables has no effect.
Customization
Tailwind CSS
<script setup lang="ts">import { Button, Tooltip } from '@hareui/vue'</script><template> <Tooltip :delay="0" text="Copied to clipboard" :ui="{ base: 'rounded-lg border border-border/80 bg-surface px-2.5 py-1 text-xs text-foreground shadow-sm' }"> <Button variant="secondary">Share link</Button> </Tooltip></template>Global Configuration
To customize the Tooltip classes for the whole app, extend the tooltip theme in createHareUI:
app.use(createHareUI({
ui: {
tooltip: {
slots: { base: 'rounded-xl shadow-lg' },
},
},
}))Styling Reference
ui key | data-slot | Element |
|---|---|---|
base | — | The floating content (Reka TooltipContent) |
arrow | overlay-arrow | The arrow SVG, rendered when arrow is true |
Interactive States
data-[state=delayed-open]/data-[state=instant-open]— applied while the tooltip is open (entrance animation), mapped from HeroUI's[data-entering]data-[state=closed]— applied while closing (exit animation), mapped from HeroUI's[data-exiting]data-[side="top|right|bottom|left"]— the resolved placement, mapped from HeroUI's[data-placement]
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | - | The text content of the tooltip. Ignored when the content slot is used. |
open | boolean | undefined | The controlled open state of the tooltip. Use with v-model:open. |
defaultOpen | boolean | false | The open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. |
delay | number | 1500 | Delay in milliseconds before the tooltip opens after the pointer enters the trigger. |
closeDelay | number | 500 | Delay in milliseconds before the tooltip closes after the pointer leaves the trigger (and the tooltip). Blur, press, Escape and opening another tooltip still close it immediately, as in React Aria. |
trigger | "focus" | "hover" | "hover" | What opens the tooltip. 'hover' opens it on hover and on focus; 'focus' opens it only when
the trigger receives focus (hovering neither opens nor closes it). |
side | "top" | "bottom" | "right" | "left" | "top" | The preferred side of the trigger to render against. |
align | "center" | "start" | "end" | "center" | The preferred alignment against the trigger. |
sideOffset | number | 7 when `arrow` is true, otherwise 3 | The distance in pixels from the trigger. |
arrow | boolean | false | Whether to render an arrow pointing at the trigger. |
disabled | boolean | false | Whether the tooltip is disabled. |
portal | string | boolean | HTMLElement | true | Where the tooltip is teleported. true teleports to body, false renders in place,
a string or element teleports to that target. |
ui | ComponentSlots<{ slots: { base: string[]; arrow: string; }; }> | - | Per-slot class overrides. |
Slots
| Slot | Props | Description |
|---|---|---|
default | { open: boolean; } | The trigger element. |
content | any | Replaces the default text content. |
Emits
| Event | Payload | Description |
|---|---|---|
update:open | [value: boolean] | - |



