Tooltip
MD3 tooltip: a short label (plain) or a richer card with a subhead and actions (rich), attached to a single trigger element. The popup renders in the top layer and flips / clamps to stay inside the viewport.
Plain
Hover the trigger, focus it with the keyboard, or long-press it on touch. Plain tooltips briefly describe icon buttons and other compact controls.
<Tooltip text="More information">
<IconButton icon={<Info />} aria-label="Info" variant="standard" />
</Tooltip>
Rich
A rich tooltip adds a subhead and up to two text-button actions, laid
out side by side.
<Tooltip
variant="rich"
subhead="Rich tooltip"
text="Rich tooltips support a subhead, longer body text, and an action."
action={<Button variant="text" size="xs">Learn more</Button>}
>
<Button variant="outlined">Hover or focus me</Button>
</Tooltip>
Persistent
persistent rich tooltips open and close on click / tap of the trigger
(hover, focus and long-press don't open them) and stay open until the user
presses elsewhere, presses Escape, or moves focus away.
Recommended when the tooltip has an action; avoid it on icon buttons.
<Tooltip
variant="rich"
persistent
subhead="New: shared albums"
text="Invite people to add their photos to an album you share with them."
action={<Button variant="text" size="xs">Learn more</Button>}
>
<Button variant="filled">Share album</Button>
</Tooltip>
Placement
placement is the preferred side — 'top' by default for plain tooltips,
'bottom' for rich ones. The tooltip flips to the other side when there is
no room and is always kept inside the viewport. Inside a top app bar, use
'bottom'.
<Tooltip text="Shown below" placement="bottom">
<Button variant="tonal">Bottom</Button>
</Tooltip>
Controlled
Visibility is uncontrolled by default (defaultOpen); pass open together
with onOpenChange to control it.
const [open, setOpen] = useState(false)
<Tooltip text="Copy link" open={open} onOpenChange={setOpen}>
<Button>Share</Button>
</Tooltip>
Accessibility
- Opens on hover, keyboard focus, or touch long-press, and stays open while the pointer or focus is on the trigger or on the tooltip itself. It closes 1.5 s after they leave, on Escape (wherever focus is), or on a press elsewhere — hoverable, dismissible and persistent per WCAG 1.4.13. Only one tooltip is open at a time.
- While open, the trigger gets
aria-describedbypointing at the popup, which hasrole="tooltip". - A rich tooltip with an
actionbecomes a non-modalrole="dialog"labelled by its subhead, and its actions are reachable with Tab from the trigger. - The tooltip describes its trigger; it does not name it. Icon-only
triggers still need their own
aria-label.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
action | ReactNode | — | Rich tooltip action(s) — up to two text buttons, laid out side by side. A rich tooltip with an action becomes a non-modal `role="dialog"` whose actions are reachable with Tab from the trigger; consider `persistent`. |
children * | ReactElement<unknown, string | JSXElementConstructor<any>> | — | The trigger element (a single focusable/hoverable element). |
defaultOpen | boolean | false | Uncontrolled initial visibility. |
onOpenChange | ((open: boolean) => void) | — | Notified when visibility should change. |
open | boolean | — | Controlled visibility. |
persistent | boolean | false | Persistent rich tooltip: opens and closes on click / tap of the trigger (hover, focus and long-press don't open it) and stays open until the user interacts elsewhere (outside press, Escape, or focus leaving the trigger and tooltip). Recommended when the tooltip has an `action`; avoid it on icon buttons. |
placement | enum | 'top' for plain, 'bottom' for rich | Preferred placement relative to the trigger. The tooltip flips to the other side when there is no room and is kept inside the viewport. Inside a top app bar, use `'bottom'`. |
subhead | ReactNode | — | Rich tooltip subhead (title). |
text | ReactNode | — | Body / supporting text. |
variant | enum | plain | Plain style or rich (with subhead + action). |