Tooltip
A popup that labels its trigger on hover or focus, built on Base UI. Positioned on any side, with an arrow by default.
Playground
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
Hover me
</TooltipTrigger>
<TooltipContent>Open source by default</TooltipContent>
</Tooltip>Preview
Wrap the app (or a subtree) in TooltipProvider, then compose Tooltip, TooltipTrigger and TooltipContent. The trigger's render prop merges it onto your own element.
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
Hover me
</TooltipTrigger>
<TooltipContent>
Open source by default <Diamond className="size-3 shrink-0" />
</TooltipContent>
</Tooltip>
</TooltipProvider>Installation
npx shadcn@latest add https://espresso-base-ui.vercel.app/r/tooltip.jsonUsage
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
Trigger
</TooltipTrigger>
<TooltipContent side="bottom">Label</TooltipContent>
</Tooltip>
</TooltipProvider>Variants
variant on TooltipContent: default is the primary-colored bubble with an arrow, plain drops the arrow, and subtle swaps to a background-colored surface.
<TooltipContent variant="default">Default variant</TooltipContent>
<TooltipContent variant="plain">Plain variant</TooltipContent>
<TooltipContent variant="subtle">Subtle variant</TooltipContent>Placement
side places the tooltip on any edge of the trigger, align shifts it along that edge, and sideOffset / alignOffset fine-tune the distance – the enter animation follows the chosen side.
<TooltipContent side="top">Top tooltip</TooltipContent>
<TooltipContent side="bottom">Bottom tooltip</TooltipContent>
<TooltipContent side="right" align="start" sideOffset={8}>
Right, aligned to start
</TooltipContent>Custom triggers
Any element works as a trigger via render – here an inline text span; long content wraps within the bubble's max-w-xs.
Hover over the underlined text to see the tooltip.
<p>
Hover over the{" "}
<Tooltip>
<TooltipTrigger
render={<span className="cursor-help underline decoration-dotted" />}
>
underlined text
</TooltipTrigger>
<TooltipContent>This is an inline tooltip</TooltipContent>
</Tooltip>{" "}
to see the tooltip.
</p>API reference
All four parts are thin wrappers around the Base UI tooltip and accept its full prop surface – TooltipProvider and Tooltip configure timing and open state, TooltipContent bundles the portal, positioner, popup and arrow into one styled component.
TooltipProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| delay | number | 150 | Milliseconds to wait before opening a tooltip; tooltips under one provider share it and skip the delay when moving between triggers. |
TooltipContent
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "plain" | "subtle" | "default" | default: primary bubble with arrow. plain: same bubble, no arrow. subtle: background-colored surface with arrow. |
| side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "top" | Which side of the trigger to place against. |
| sideOffset | number | 4 | Gap in pixels between trigger and tooltip. |
| align | "start" | "center" | "end" | "center" | Alignment along the chosen side. |
| alignOffset | number | 0 | Extra offset in pixels along the align axis. |
| Component | Description |
|---|---|
| TooltipProvider | Shares open delay and timing across tooltips (data-slot="tooltip-provider"). Mount once around the subtree. |
| Tooltip | Root that pairs one trigger with its content (data-slot="tooltip"). Accepts Base UI root props like open, onOpenChange and defaultOpen. |
| TooltipTrigger | The hoverable/focusable element (data-slot="tooltip-trigger"). Use render to merge onto your own element, e.g. a Button. |
| TooltipContent | Portalled, positioned popup (data-slot="tooltip-content") that also reflects data-variant. Renders the arrow unless variant="plain". |
Accessibility & styling hooks
The Base UI primitive wires the trigger and popup together with the proper ARIA attributes, opens on keyboard focus as well as hover, and closes on Escape – so tooltip content stays discoverable without a pointer. Keep the content to a short label; interactive controls don't belong in a tooltip. For styling, every part exposes its data-slot, and the popup reflects Base UI state as data-open / data-closed plus data-side for per-placement animation – embedded Kbd elements (matched via data-slot="kbd") get tightened trailing padding automatically.