Tooltip
StableA short text label that appears on hover and keyboard focus to name an icon-only control or reveal its shortcut.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/tooltip.jsonWrap your app once in TooltipProvider. It gives every tooltip the same delay and lets neighbors open instantly once one is showing — hover along a toolbar and the labels follow without waiting.
import { TooltipProvider } from "@/components/ui/tooltip"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<TooltipProvider delay={400}>{children}</TooltipProvider>
</body>
</html>
)
}Usage
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"<Tooltip>
<TooltipTrigger render={<Button variant="outline" size="icon" aria-label="Add to library" />}>
<PlusIcon />
</TooltipTrigger>
<TooltipContent>Add to library</TooltipContent>
</Tooltip>prfct tooltips are inverted — gray-1 text on a gray-12 fill — so they read as a label on top of any surface in either mode, and enter faster than other popups (160ms) because they appear dozens of times per session.
Examples
Keyboard shortcuts
Tooltips are the natural place to teach shortcuts. Put a Kbd after the label; it restyles itself for the inverted surface. Hover along the toolbar: after the first tooltip opens, the next ones appear immediately.
Side
side places the tooltip on the top (default), right, bottom or left of its trigger. It flips automatically near the viewport edges.
Disabled triggers
A disabled button receives no pointer or focus events, so a tooltip on it could never open. Keep it reachable with focusableWhenDisabled and use the tooltip to explain what's missing.
Delay
The provider sets a 400ms delay for every tooltip. Override it per trigger with delay — shorter for dense tools people scan quickly, longer where accidental tooltips would get in the way.
Guidelines
When to use
- To name icon-only buttons. Every icon button needs an
aria-label; the tooltip shows the same text to sighted pointer users. - To reveal keyboard shortcuts for common actions.
- To expand truncated text, such as a long file name.
When not to use
- For information people need to complete a task — show it on the page. Tooltips are invisible until someone thinks to look.
- For interactive content: links, buttons, inputs. Tooltips disappear when the pointer leaves — use a Popover.
- For rich previews — use a Hover Card.
- On touch-only interfaces, where there is no hover — make the label visible instead.
Write labels, not sentences
A tooltip is read in a glance: two to four words, sentence case, no trailing period. Describe the action, not the icon.
Accessibility
A tooltip is a visual hint for sighted pointer and keyboard users. It opens on hover and on keyboard focus and never takes focus itself — but Base UI doesn't wire it to the trigger with aria-describedby, so screen readers don't announce it. The trigger's own accessible name has to carry the meaning.
| Key | Behavior |
|---|---|
Tab | Focusing the trigger opens the tooltip after the delay. |
Esc | Closes the tooltip without moving focus. |
- Put the label in the accessible name. Give icon-only triggers an
aria-labelwith the same text as the tooltip. A tooltip supplements the name; it never replaces it. - Hoverable. Moving the pointer onto the tooltip keeps it open, so people using screen magnification can read it (WCAG 1.4.13). Set
disableHoverablePopuponly for tooltips that follow the cursor. - Dismissible. Esc hides it without moving the pointer or focus.
- Plain text only. Content inside a tooltip isn't reachable with the keyboard; never put links or buttons in it.
API reference
TooltipProvider
Shares a delay between tooltips. Doesn't render an element.
Tooltip
The root. Doesn't render an element. Accepts every prop of Base UI Tooltip.Root.
TooltipTrigger
The element that shows the tooltip. Renders a <button>; use render to render a prfct Button.
TooltipContent
Renders the portal, positioner and inverted popup with an arrow.