A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
import React from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { PlusIcon } from '@radix-ui/react-icons';
import './styles.css';
const TooltipDemo = () => {
return (
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<button className="IconButton">
<PlusIcon />
</button>
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content className="TooltipContent" sideOffset={5}>
Add to library
<Tooltip.Arrow className="TooltipArrow" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
};
export default TooltipDemo;
Install the component from your command line.
npm install @radix-ui/react-tooltip
Import all parts and piece them together.
import * as Tooltip from '@radix-ui/react-tooltip';
export default () => (
<Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger />
<Tooltip.Portal>
<Tooltip.Content>
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
);
Wraps your app to provide global functionality to your tooltips.
Prop | Type | Default |
---|---|---|
delayDuration | number | 700 |
skipDelayDuration | number | 300 |
disableHoverableContent | boolean |
Contains all the parts of a tooltip.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean | |
onOpenChange | function | |
delayDuration | number | 700 |
disableHoverableContent | boolean |
The button that toggles the tooltip. By default, the Tooltip.Content
will position itself against the trigger.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "closed" | "delayed-open" | "instant-open" |
When used, portals the content part into the body
.
Prop | Type | Default |
---|---|---|
forceMount | boolean | |
container | HTMLElement | document.body |
The component that pops out when the tooltip is open.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
aria-label | string | |
onEscapeKeyDown | function | |
onPointerDownOutside | function | |
forceMount | boolean | |
side | enum | "top" |
sideOffset | number | 0 |
align | enum | "center" |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionBoundary | Boundary | [] |
collisionPadding | number | Padding | 0 |
arrowPadding | number | 0 |
sticky | enum | "partial" |
hideWhenDetached | boolean | false |
Data Attribute | Values |
---|---|
[data-state] | "closed" | "delayed-open" | "instant-open" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
CSS Variable | Description |
---|---|
--radix-tooltip-content-transform-origin | The transform-origin computed from the content and arrow positions/offsets |
An optional arrow element to render alongside the tooltip. This can be used to help visually link the trigger with the Tooltip.Content
. Must be rendered inside Tooltip.Content
.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
width | number | 10 |
height | number | 5 |
Use the Provider
to control delayDuration
and skipDelayDuration
globally.
import * as Tooltip from '@radix-ui/react-tooltip';
export default () => (
<Tooltip.Provider delayDuration={800} skipDelayDuration={500}>
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<Tooltip.Content>…</Tooltip.Content>
</Tooltip.Root>
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<Tooltip.Content>…</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
);
Use the delayDuration
prop to control the time it takes for the tooltip to open.
import * as Tooltip from '@radix-ui/react-tooltip';
export default () => (
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>…</Tooltip.Trigger>
<Tooltip.Content>…</Tooltip.Content>
</Tooltip.Root>
);
Since disabled buttons don't fire events, you need to:
Render the Trigger
as span
.
Ensure the button
has no pointerEvents
.
import * as Tooltip from '@radix-ui/react-tooltip';
export default () => (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<span tabIndex={0}>
<button disabled style={{ pointerEvents: 'none' }}>
…
</button>
</span>
</Tooltip.Trigger>
<Tooltip.Content>…</Tooltip.Content>
</Tooltip.Root>
);
We expose a CSS custom property --radix-tooltip-content-transform-origin
. Use it to animate the content from its computed origin based on side
, sideOffset
, align
, alignOffset
and any collisions.
// index.jsx
import * as Tooltip from '@radix-ui/react-tooltip';
import './styles.css';
export default () => (
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<Tooltip.Content className="TooltipContent">…</Tooltip.Content>
</Tooltip.Root>
);
/* styles.css */
.TooltipContent {
transform-origin: var(--radix-tooltip-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}
We expose data-side
and data-align
attributes. Their values will change at runtime to reflect collisions. Use them to create collision and direction-aware animations.
// index.jsx
import * as Tooltip from '@radix-ui/react-tooltip';
import './styles.css';
export default () => (
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<Tooltip.Content className="TooltipContent">…</Tooltip.Content>
</Tooltip.Root>
);
/* styles.css */
.TooltipContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.TooltipContent[data-side='top'] {
animation-name: slideUp;
}
.TooltipContent[data-side='bottom'] {
animation-name: slideDown;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Key | Description |
---|---|
Tab | Opens/closes the tooltip without delay. |
Space | If open, closes the tooltip without delay. |
Enter | If open, closes the tooltip without delay. |
Escape | If open, closes the tooltip without delay. |
Create your own API by abstracting the primitive parts into your own component.
This example abstracts all of the Tooltip
parts and introduces a new content
prop.
import { Tooltip } from './your-tooltip';
export default () => (
<Tooltip content="Tooltip content">
<button>Tooltip trigger</button>
</Tooltip>
);
Use the asChild
prop to convert the trigger part into a slottable area. It will replace the trigger with the child that gets passed to it.
// your-tooltip.jsx
import React from 'react';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
export function Tooltip({
children,
content,
open,
defaultOpen,
onOpenChange,
...props
}) {
return (
<TooltipPrimitive.Root
open={open}
defaultOpen={defaultOpen}
onOpenChange={onOpenChange}
>
<TooltipPrimitive.Trigger asChild>
{children}
</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content side="top" align="center" {...props}>
{content}
<TooltipPrimitive.Arrow width={11} height={5} />
</TooltipPrimitive.Content>
</TooltipPrimitive.Root>
);
}