Examples
Hover and focus
The same supplemental message is available from pointer hover and keyboard focus.
Tooltip available on hover and focus
"use client";
import { HelpCircle } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="What is a workspace?" variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent> A workspace keeps a team's settings together.</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
Size and multiline
Use sm for a short hint, md (the default) when the message needs two short lines, and lg when it needs a little more room.
Tooltip small, medium, and large sizes
"use client";
import { Button } from "@phuctech/ui/components/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<div className="flex flex-wrap items-center gap-4">
<Tooltip>
<TooltipTrigger render={<Button variant="outline">Small</Button>} />
<TooltipContent size="sm">Concise supplemental hint.</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger render={<Button variant="outline">Medium</Button>} />
<TooltipContent size="md">The default size fits a supplemental message that needs two short lines to stay readable.</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger render={<Button variant="outline">Large</Button>} />
<TooltipContent size="lg">Use the largest size when the supplemental message needs a little more room to breathe.</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);
}
Supported positions
Use side and align props when the trigger needs a different placement.
Tooltip placement comparison
"use client";
import { HelpCircle } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<div className="flex flex-wrap items-center justify-center gap-3">
{(["top", "right", "bottom", "left"] as const).map((side) => (
<Tooltip key={side}>
<TooltipTrigger render={<IconButton aria-label={`${side} placement`} variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent side={side} align="center">{side} placement</TooltipContent>
</Tooltip>
))}
</div>
</TooltipProvider>
);
}
Provider timing
Coordinate a related group of tooltips with shared delay and close timing.
Two Tooltips using one Provider
"use client";
import { HelpCircle, Info } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider delay={450} closeDelay={100}>
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="Workspace help" variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent>Shared provider timing applies to this group.</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="Workspace status" variant="outline"><Info aria-hidden="true" /></IconButton>} />
<TooltipContent>Move between related triggers to reuse the provider.</TooltipContent>
</Tooltip>
</div>
</TooltipProvider>
);
}
Arrow and shortcut
An optional arrow and a keyboard shortcut can reinforce a short, supplemental message.
Tooltip with arrow and keyboard shortcut
"use client";
import { HelpCircle } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="Keyboard shortcut" variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent side="right" className="flex items-center gap-2">
Save quickly <kbd className="rounded border border-background/30 px-1.5 py-0.5 text-[0.7rem]">⌘ S</kbd><TooltipArrow />
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
Controlled visibility
Feature state can control visibility when the surrounding interaction needs coordination.
Controlled Tooltip preview
"use client";
import { useState } from "react";
import { Button } from "@phuctech/ui/components/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
const [open, setOpen] = useState(true);
return (
<TooltipProvider>
<div className="grid justify-items-center gap-2">
<Tooltip open={open} onOpenChange={setOpen}>
<TooltipTrigger render={<Button variant="outline">Controlled help</Button>} />
<TooltipContent>Visibility is coordinated by feature state.</TooltipContent>
</Tooltip>
<Button size="sm" variant="ghost" onClick={() => setOpen((value) => !value)}>
{open ? "Hide" : "Show"} tooltip
</Button>
</div>
</TooltipProvider>
);
}
Short, bounded content
Keep supplemental text concise and within the popup max width.
Tooltip with bounded content
"use client";
import { HelpCircle } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="Show automation context" variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent>Short context remains supplemental and wraps within a bounded width.</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
When to use
Use Tooltip for a short clarification, a keyboard shortcut, or context for an icon-only control. Use visible helper text when the information affects task completion, and use Dialog or Popover for interactive content.
Usage
"use client";
import { HelpCircle } from "@phuctech/ui/icons";
import { IconButton } from "@phuctech/ui/components/icon-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@phuctech/ui/components/tooltip";
export function Example() {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger render={<IconButton aria-label="What is a workspace?" variant="outline"><HelpCircle aria-hidden="true" /></IconButton>} />
<TooltipContent> A workspace keeps a team's settings together.</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
Wrap related tooltips in TooltipProvider. Compose the trigger with Button or IconButton through Base UI's render prop to avoid nested buttons. Use TooltipArrow only when the visual connection helps; it is decorative.
Behavior
- Pointer hover opens after the provider/trigger delay; keyboard focus opens immediately enough to be discoverable.
- Escape and leaving the trigger close the tooltip; the trigger remains the focus owner.
- Tooltip content is portaled and positioned against the trigger so nearby overflow does not clip it.
API
Tokens
Accessibility
- The trigger must have its own accessible name; tooltip text is supplementary.
- Verify the tooltip opens from keyboard focus, not only a mouse hover.
- Never put a required instruction, error, or interactive form only inside a tooltip.
Do and don’t
Explain an unfamiliar icon in one short sentence and keep the trigger usable without it.
Use a tooltip for a paragraph, a required form label, or an action that needs a click.
Related
- IconButton is a common tooltip trigger for icon-only actions.
- Dialog handles longer tasks and focus-managed content.