Anchored hint that appears on hover or focus.
Introduction
Tooltips show a short hint anchored to a control. They open on hover or focus when you provide a trigger, and close when the pointer leaves or focus moves away.
Use content for plain text, or pass custom markup as children (default slot on Vue). Children win when both are set. Put the opener in the trigger slot, or pass anchorEl for an external anchor.
Tooltip vs Menu: Use Tooltip for brief labels and help text. Use Menu for action lists and interactive choices.
Import
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";import { Tooltip } from "@bridge-ui/react/Components/Tooltip";Basic usage
Provide a trigger via the trigger slot (Vue) or slots.trigger (React). Hover or focus the button to open the tooltip.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";
</script>
<template>
<div class="flex justify-center py-8">
<Tooltip content="Save changes">
<template #trigger>
<Button>Save</Button>
</template>
</Tooltip>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Tooltip } from "@bridge-ui/react/Components/Tooltip";
export default function TooltipBasic() {
return (
<div className="flex justify-center py-8">
<Tooltip
content="Save changes"
slots={{
trigger: <Button>Save</Button>,
}}
/>
</div>
);
}
External anchor
Pass anchorEl to position against an element outside the tooltip instead of using the trigger slot. Without a trigger, open/close is controlled (show / v-model)—wire hover or focus on the anchor yourself.
<script setup lang="ts">
import { ref, useTemplateRef } from "vue";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";
const open = ref(false);
const anchorEl = useTemplateRef<HTMLElement>("anchorEl");
</script>
<template>
<div class="flex justify-center py-8">
<span
ref="anchorEl"
class="inline-flex"
v-on:focus="open = true"
v-on:blur="open = false"
v-on:pointerenter="open = true"
v-on:pointerleave="open = false"
>
<Button>Hover me</Button>
</span>
<Tooltip
v-model="open"
:open-delay="0"
:anchor-el="anchorEl"
content="Anchored tip"
/>
</div>
</template>
import { useRef, useState } from "react";
import { Button } from "@bridge-ui/react/Components/Button";
import { Tooltip } from "@bridge-ui/react/Components/Tooltip";
export default function TooltipAnchorEl() {
const [open, setOpen] = useState(false);
const anchorRef = useRef<HTMLSpanElement>(null);
return (
<div className="flex justify-center py-8">
<span
ref={anchorRef}
className="inline-flex"
onBlur={() => setOpen(false)}
onFocus={() => setOpen(true)}
onPointerEnter={() => setOpen(true)}
onPointerLeave={() => setOpen(false)}
>
<Button>Hover me</Button>
</span>
<Tooltip
show={open}
openDelay={0}
anchorEl={anchorRef}
content="Anchored tip"
onShowChange={setOpen}
/>
</div>
);
}
Placement
Use placement to position the panel relative to the trigger. The default is top.
<script setup lang="ts">
import type { PositionPlacement } from "@bridge-ui/core";
import { Button } from "@bridge-ui/vue/Components/Button";
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";
const placements: PositionPlacement[] = [
"top-start",
"top",
"top-end",
"right-start",
"right",
"right-end",
"bottom-start",
"bottom",
"bottom-end",
"left-start",
"left",
"left-end",
];
</script>
<template>
<div class="flex flex-wrap items-center justify-center gap-3 py-8">
<Tooltip
:key="placement"
:content="placement"
:placement="placement"
v-for="placement in placements"
>
<template #trigger>
<Button variant="outline">{{ placement }}</Button>
</template>
</Tooltip>
</div>
</template>
import type { PositionPlacement } from "@bridge-ui/core";
import { Button } from "@bridge-ui/react/Components/Button";
import { Tooltip } from "@bridge-ui/react/Components/Tooltip";
const placements: PositionPlacement[] = [
"top-start",
"top",
"top-end",
"right-start",
"right",
"right-end",
"bottom-start",
"bottom",
"bottom-end",
"left-start",
"left",
"left-end",
];
export default function TooltipPlacement() {
return (
<div className="flex flex-wrap items-center justify-center gap-3 py-8">
{placements.map((placement) => (
<Tooltip
key={placement}
content={placement}
placement={placement}
slots={{
trigger: <Button variant="outline">{placement}</Button>,
}}
/>
))}
</div>
);
}
Color and size
Style the panel with color, size, and arrow. Default color is dark; size defaults to md.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";
</script>
<template>
<div class="flex flex-wrap items-center justify-center gap-4 py-8">
<Tooltip content="Dark tip">
<template #trigger>
<Button variant="outline">Dark</Button>
</template>
</Tooltip>
<Tooltip color="primary" content="Primary tip">
<template #trigger>
<Button>Primary</Button>
</template>
</Tooltip>
<Tooltip size="lg" color="success" content="Success tip">
<template #trigger>
<Button color="success">Success</Button>
</template>
</Tooltip>
<Tooltip color="error" :arrow="false" content="No arrow">
<template #trigger>
<Button color="error" variant="outline">No arrow</Button>
</template>
</Tooltip>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Tooltip } from "@bridge-ui/react/Components/Tooltip";
export default function TooltipColors() {
return (
<div className="flex flex-wrap items-center justify-center gap-4 py-8">
<Tooltip
content="Dark tip"
slots={{
trigger: <Button variant="outline">Dark</Button>,
}}
/>
<Tooltip
color="primary"
content="Primary tip"
slots={{
trigger: <Button>Primary</Button>,
}}
/>
<Tooltip
size="lg"
color="success"
content="Success tip"
slots={{
trigger: <Button color="success">Success</Button>,
}}
/>
<Tooltip
arrow={false}
color="error"
content="No arrow"
slots={{
trigger: (
<Button color="error" variant="outline">
No arrow
</Button>
),
}}
/>
</div>
);
}
Custom body
Pass children (React) or the default slot (Vue) for custom panel content. This overrides content when both are set.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Tooltip } from "@bridge-ui/vue/Components/Tooltip";
</script>
<template>
<div class="flex justify-center py-8">
<Tooltip>
<template #trigger>
<Button variant="outline">Custom body</Button>
</template>
<span class="font-semibold">Bold label</span>
<span class="mt-0.5 block text-xs opacity-80">
Extra detail in the panel
</span>
</Tooltip>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Tooltip } from "@bridge-ui/react/Components/Tooltip";
export default function TooltipCustom() {
return (
<div className="flex justify-center py-8">
<Tooltip
slots={{
trigger: <Button variant="outline">Custom body</Button>,
}}
>
<span className="font-semibold">Bold label</span>
<span className="mt-0.5 block text-xs opacity-80">
Extra detail in the panel
</span>
</Tooltip>
</div>
);
}
Related components
Accessibility
- Prefer a focusable trigger (for example a Button) so keyboard users can open the tooltip on focus.
- Keep tooltip copy short; do not put critical information only in a tooltip.
- Hover and focus open the panel after openDelay (default 200 ms); leave/blur closes after closeDelay.
- When disabled is true, the tooltip does not open.
Anatomy
<div> <!-- root (when trigger is used) -->
<div> <!-- trigger wrapper -->
<div> <!-- portaled content -->
…panel body…
<div> <!-- arrow (optional) -->
</div>
</div>The panel is portaled to teleportTo (default "body") and positioned relative to the trigger or anchorEl.
API
| Prop | Type | Default | Description |
|---|---|---|---|
| v-model | boolean | false | Whether the tooltip is visible. Omit for uncontrolled hover/focus with #trigger. |
| @show-change | (show: boolean) => void | — | Emitted when visibility should change. |
| Slot | Description |
|---|---|
| default | Custom panel body. Wins over content when both are set. |
| trigger | The trigger element that opens the tooltip. |
| Prop | Type | Default | Description |
|---|---|---|---|
| show | boolean | — | Whether the tooltip is visible. Omit for uncontrolled hover/focus when using slots.trigger. |
| onShowChange | (show: boolean) => void | — | Called when show should change. |
| children | ReactNode | — | Custom panel body. Wins over content when both are set. |
| slots | TooltipSlots | — | Optional trigger slot for the opener. |
| Prop | Type | Default | Description |
|---|---|---|---|
| content | string | — | Plain text for the tooltip panel. Prefer children/default slot for custom markup. |
| anchorEl | HTMLElement | RefObject<HTMLElement | null> | null | — | External anchor for positioning instead of the trigger. |
| arrow | boolean | true | Whether the tooltip shows an arrow pointing at the trigger. |
| color | TooltipColor | "dark" | Semantic color of the tooltip panel. |
| size | TooltipSize | "md" | Padding and typography of the tooltip panel. |
| rounded | TooltipRounded | "md" | Border radius of the tooltip panel. |
| placement | PositionPlacement | "top" | Preferred placement relative to the anchor. |
| offset | number | 8 | Gap between the trigger and the tooltip panel (px). |
| openDelay | number | 200 | Delay in ms before opening after pointer enter / focus. |
| closeDelay | number | 0 | Delay in ms before closing after pointer leave / blur. |
| disabled | boolean | false | When true, the tooltip does not open on hover or focus. |
| strategy | PositionStrategy | "fixed" | CSS position strategy for the floating panel. |
| teleportTo | string | false | "body" | Portal target. Pass false to render in place. |
| classes | TooltipClasses | — | Class overrides for root, trigger, content, and arrow. |
| customProps | TooltipCustomProps | — | Extra props for internal parts. |