A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
"use client";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function TooltipDemo() {
return (
<Tooltip>
<TooltipTrigger render={<Button variant="tertiary" />}>
Hover
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
);
}
pnpm dlx shadcn@latest add https://herocn.dev/r/tooltip.jsonTooltipProvider to the root of your app.import { TooltipProvider } from "@/components/ui/tooltip"
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body>
<TooltipProvider>{children}</TooltipProvider>
</body>
</html>
)
}import { Button } from "@/components/ui/button"
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>Hover</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent>
</Tooltip>Use the following composition to build a Tooltip:
Tooltip
├── TooltipTrigger
└── TooltipContentUse the side prop to change the position of the tooltip.
"use client";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function TooltipSides() {
return (
<div className="flex flex-wrap gap-2">
{(["left", "top", "bottom", "right"] as const).map((side) => (
<Tooltip key={side}>
<TooltipTrigger
render={<Button variant="tertiary" className="w-fit capitalize" />}
>
{side}
</TooltipTrigger>
<TooltipContent side={side}>
<p>Add to library</p>
</TooltipContent>
</Tooltip>
))}
</div>
);
}
Use the withArrow prop to add arrow to the tooltip
"use client";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function ToolTipArrow() {
return (
<div className="flex flex-wrap gap-2">
{(["left", "top", "bottom", "right"] as const).map((side) => (
<Tooltip key={side}>
<TooltipTrigger
render={<Button variant="tertiary" className="w-fit capitalize" />}
>
{side}
</TooltipTrigger>
<TooltipContent withArrow side={side}>
<p>This tooltip has an arrow</p>
</TooltipContent>
</Tooltip>
))}
</div>
);
}
"use client";
import { SaveIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Kbd } from "@/components/ui/kbd";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function TooltipKeyboard() {
return (
<Tooltip>
<TooltipTrigger render={<Button variant="tertiary" size="icon-sm" />}>
<SaveIcon />
</TooltipTrigger>
<TooltipContent>
Save Changes <Kbd>S</Kbd>
</TooltipContent>
</Tooltip>
);
}
Show a tooltip on a disabled button by wrapping it with a span.
"use client";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
export function TooltipDisabled() {
return (
<Tooltip>
<TooltipTrigger render={<span className="inline-block w-fit" />}>
<Button variant="tertiary" disabled>
Disabled
</Button>
</TooltipTrigger>
<TooltipContent>
<p>This feature is currently unavailable</p>
</TooltipContent>
</Tooltip>
);
}
To enable RTL support in shadcn/ui, see the RTL configuration guide.
"use client";
import {
type Translations,
useTranslation,
} from "@/components/language-selector";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
const translations: 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;
export function TooltipRtl() {
const { dir, language, t } = useTranslation(translations, "ar");
return (
<div lang={language} dir={dir} className="grid gap-4">
<div className="flex flex-wrap justify-center gap-2">
{physicalSides.map((side) => (
<Tooltip key={side}>
<TooltipTrigger render={<Button variant="tertiary" />}>
{t[side]}
</TooltipTrigger>
<TooltipContent side={side} dir={dir}>
{t.content}
</TooltipContent>
</Tooltip>
))}
</div>
<div className="flex flex-wrap justify-center gap-2">
{logicalSides.map((side) => (
<Tooltip key={side}>
<TooltipTrigger render={<Button variant="tertiary" />}>
{t[side]}
</TooltipTrigger>
<TooltipContent side={side} dir={dir}>
{t.content}
</TooltipContent>
</Tooltip>
))}
</div>
</div>
);
}
The tooltip is built on Base UI Tooltip. TooltipContent sets default positioning and passes the remaining props to the popup.
| Prop | Type | Default |
|---|---|---|
| align | "start" | "center" | "end" | "center" |
| alignOffset | number | 0 |
| side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "top" |
| withArrow | boolean | false |
| sideOffset | number | 4 |
| Prop | Type | Default |
|---|---|---|
| delay | number | 0 |
See the Base UI Tooltip API reference for root, trigger, and other props.