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 { styled, keyframes } from '@stitches/react';
import { PlusIcon } from '@radix-ui/react-icons';
import { violet, blackA } from '@radix-ui/colors';
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
const slideUpAndFade = keyframes({
'0%': { opacity: 0, transform: 'translateY(2px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
});
const slideRightAndFade = keyframes({
'0%': { opacity: 0, transform: 'translateX(-2px)' },
'100%': { opacity: 1, transform: 'translateX(0)' },
});
const slideDownAndFade = keyframes({
'0%': { opacity: 0, transform: 'translateY(-2px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
});
const slideLeftAndFade = keyframes({
'0%': { opacity: 0, transform: 'translateX(2px)' },
'100%': { opacity: 1, transform: 'translateX(0)' },
});
const StyledContent = styled(TooltipPrimitive.Content, {
borderRadius: 4,
padding: '10px 15px',
fontSize: 15,
lineHeight: 1,
color: violet.violet11,
backgroundColor: 'white',
boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
'@media (prefers-reduced-motion: no-preference)': {
animationDuration: '400ms',
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
animationFillMode: 'forwards',
willChange: 'transform, opacity',
'&[data-state="delayed-open"]': {
'&[data-side="top"]': { animationName: slideDownAndFade },
'&[data-side="right"]': { animationName: slideLeftAndFade },
'&[data-side="bottom"]': { animationName: slideUpAndFade },
'&[data-side="left"]': { animationName: slideRightAndFade },
},
},
});
const StyledArrow = styled(TooltipPrimitive.Arrow, {
fill: 'white',
});
// Exports
export const Provider = TooltipPrimitive.Provider;
export const Tooltip = TooltipPrimitive.Root;
export const TooltipTrigger = TooltipPrimitive.Trigger;
export const TooltipContent = StyledContent;
// Your app...
const IconButton = styled('button', {
all: 'unset',
fontFamily: 'inherit',
borderRadius: '100%',
height: 35,
width: 35,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
color: violet.violet11,
backgroundColor: 'white',
boxShadow: `0 2px 10px ${blackA.blackA7}`,
'&:hover': { backgroundColor: violet.violet3 },
'&:focus': { boxShadow: `0 0 0 2px black` },
});
const TooltipDemo = () => {
return (
<Tooltip>
<TooltipTrigger asChild>
<IconButton>
<PlusIcon />
</IconButton>
</TooltipTrigger>
<StyledContent sideOffset={5} >
Add to library
<StyledArrow />
</StyledContent>
</Tooltip>
);
};
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.Content>
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
);
Wraps your app to provide global functionality to your tooltips.
Prop | Type | Default |
---|---|---|
delayDuration | number | 700 |
skipDelayDuration | number | 300 |
Contains all the parts of a tooltip.
Prop | Type | Default |
---|---|---|
defaultOpen | boolean | |
open | boolean | |
onOpenChange | function | |
delayDuration | number | 700 |
The button that toggles the tooltip. By default, the Tooltip.Content
will position itself against the trigger.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
The component that pops out when the tooltip is open.
Prop | Type | Default |
---|---|---|
asChild | boolean | false |
aria-label | string | |
portalled | boolean | true |
side | enum | "bottom" |
sideOffset | number | 0 |
align | enum | "center" |
alignOffset | number | 0 |
avoidCollisions | boolean | true |
collisionTolerance | number | 0 |
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 |
offset | number |
Use the Provider
to control delayDuration
and skipDelayDuration
globally. Without the provider, tooltips will always delay before opening when moving from one tooltip trigger to another (e.g. skipDelayDuration={0}
).
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.
import { styled, keyframes } from '@stitches/react';
import * as Tooltip from '@radix-ui/react-tooltip';
const scaleIn = keyframes({
'0%': { opacity: 0, transform: 'scale(0)' },
'100%': { opacity: 1, transform: 'scale(1)' },
});
const TooltipContent = styled(Tooltip.Content, {
transformOrigin: 'var(--radix-tooltip-content-transform-origin)',
animation: `${scaleIn} 0.5s ease-out forwards`,
});
export default () => (
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<TooltipContent>…</TooltipContent>
</Tooltip.Root>
);
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.
import { styled, keyframes } from '@stitches/react';
import * as Tooltip from '@radix-ui/react-tooltip';
const slideDown = keyframes({
'0%': { opacity: 0, transform: 'translateY(-10px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
});
const slideUp = keyframes({
'0%': { opacity: 0, transform: 'translateY(10px)' },
'100%': { opacity: 1, transform: 'translateY(0)' },
});
const TooltipContent = styled(Tooltip.Content, {
animationDuration: '0.6s',
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
animationFillMode: 'forwards',
'&[data-side="top"]': { animationName: slideUp },
'&[data-side="bottom"]': { animationName: slideDown },
});
export default () => (
<Tooltip.Root>
<Tooltip.Trigger>…</Tooltip.Trigger>
<TooltipContent>…</TooltipContent>
</Tooltip.Root>
);
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 offset={5} width={11} height={5} />
</TooltipPrimitive.Content>
</TooltipPrimitive.Root>
);
}