---
import { buttonVariants } from "@/components/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
---
<Tooltip>
<TooltipTrigger class={buttonVariants({ variant: "outline" })}>
Hover
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>Installation
npx shadcn@latest add @fulldev/tooltip
Usage
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
<Tooltip>
<TooltipTrigger>Hover</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
TooltipTrigger renders an unstyled button, or an a with href. Pass
as="span" or as="div" for another element, and
class={buttonVariants({ variant: "outline" })} for button styles.
Composition
Tooltip
├── TooltipTrigger
└── TooltipContent
Side
Use the side prop on TooltipContent or Tooltip to change the position of
the tooltip. Logical sides inline-start and inline-end follow the text
direction.
---
import { buttonVariants } from "@/components/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
---
<div class="flex flex-wrap gap-2">
{(["left", "top", "bottom", "right"] as const).map((side) => (
<Tooltip>
<TooltipTrigger
class={buttonVariants({
variant: "outline",
class: "w-fit capitalize",
})}
>
{side}
</TooltipTrigger>
<TooltipContent side={side}>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
))}
</div>With Keyboard Shortcut
---
import SaveIcon from "@lucide/astro/icons/save"
import { buttonVariants } from "@/components/ui/button"
import { Kbd } from "@/components/ui/kbd"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
---
<Tooltip>
<TooltipTrigger
class={buttonVariants({ variant: "outline", size: "icon-sm" })}
>
<SaveIcon />
</TooltipTrigger>
<TooltipContent>
Save Changes <Kbd>S</Kbd>
</TooltipContent>
</Tooltip>Disabled Button
Show a tooltip on a disabled button by wrapping it with
<TooltipTrigger as="span">.
---
import { Button } from "@/components/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
---
<Tooltip>
<TooltipTrigger as="span" class="inline-block w-fit">
<Button variant="outline" disabled>
Disabled
</Button>
</TooltipTrigger>
<TooltipContent>
<p>This feature is currently unavailable</p>
</TooltipContent>
</Tooltip>RTL
---
import { buttonVariants } from "@/components/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
const translations = {
en: {
dir: "ltr",
values: {
content: "Add to library",
"inline-start": "Inline Start",
left: "Left",
top: "Top",
bottom: "Bottom",
right: "Right",
"inline-end": "Inline End",
},
},
ar: {
dir: "rtl",
values: {
content: "إضافة إلى المكتبة",
"inline-start": "بداية السطر",
left: "يسار",
top: "أعلى",
bottom: "أسفل",
right: "يمين",
"inline-end": "نهاية السطر",
},
},
he: {
dir: "rtl",
values: {
content: "הוסף לספרייה",
"inline-start": "תחילת השורה",
left: "שמאל",
top: "למעלה",
bottom: "למטה",
right: "ימין",
"inline-end": "סוף השורה",
},
},
}
const physicalSides = ["left", "top", "bottom", "right"] as const
const logicalSides = ["inline-start", "inline-end"] as const
const { dir, values: t } = translations.ar
---
<div class="grid gap-4">
<div class="flex flex-wrap justify-center gap-2">
{physicalSides.map((side) => (
<Tooltip>
<TooltipTrigger class={buttonVariants({ variant: "outline" })}>
{t[side]}
</TooltipTrigger>
<TooltipContent side={side} dir={dir}>
{t.content}
</TooltipContent>
</Tooltip>
))}
</div>
<div class="flex flex-wrap justify-center gap-2">
{logicalSides.map((side) => (
<Tooltip>
<TooltipTrigger class={buttonVariants({ variant: "outline" })}>
{t[side]}
</TooltipTrigger>
<TooltipContent side={side} dir={dir}>
{t.content}
</TooltipContent>
</Tooltip>
))}
</div>
</div>Content mounting
Data Slot’s recommended "lazy" strategy is the default: closed content is
retained but detached from the document. Fulldev initializes nested Data Slot
components inside that retained content too.
Use mountStrategy="eager" when an integration needs to query closed content
through the document. Both strategies include content in server-rendered HTML;
neither adds a no-JavaScript fallback.
API Reference
Source: src/components/ui/tooltip
Tooltip
| Prop | Type | Default |
|---|---|---|
delay | number | 300 |
skipDelayDuration | number | 300 |
side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "top" |
align | "start" | "center" | "end" | "center" |
sideOffset | number | 4 |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionPadding | number | 8 |
mountStrategy | "lazy" | "eager" | "lazy" |
TooltipContent takes the same placement props and overrides the root.
See the Data Slot tooltip docs for events and programmatic control.