Tooltip

A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.

Features

  • Built-in state machine to control display delay.
  • Opens when the trigger is focused or hovered.
  • Closes when the trigger is activated or when pressing escape.
  • Supports custom timings.

Install the component from your command line.

npm install @radix-ui/react-tooltip

Import the components and piece the parts together.

import * as Tooltip from '@radix-ui/react-tooltip';
export default () => (
<Tooltip.Root>
<Tooltip.Trigger />
<Tooltip.Content>
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip.Root>
);

Create your styled tooltip component from the primitive parts.

import { styled } from 'path-to/stitches.config';
import * as Tooltip from '@radix-ui/react-tooltip';
import { BlendingModeIcon } from '@radix-ui/react-icons';
const StyledContent = styled(Tooltip.Content, {
borderRadius: 3,
padding: '5px 10px',
fontSize: 14,
backgroundColor: 'gainsboro',
color: 'black',
});
const StyledArrow = styled(Tooltip.Arrow, {
fill: 'gainsboro',
});
export default () => (
<Tooltip.Root>
<Tooltip.Trigger>
<BlendingModeIcon />
</Tooltip.Trigger>
<StyledContent>
Blending mode
<StyledArrow />
</StyledContent>
</Tooltip.Root>
);

Contains all the parts of a tooltip.

PropTypeDefault
defaultOpenbooleanNo default value
openbooleanNo default value
onOpenChangefunctionNo default value
delayDurationnumber700
skipDelayDurationnumber300

The button that toggles the tooltip. By default, the Tooltip.Content will position itself against the trigger.

PropTypeDefault
asenumbutton

The component that pops out when the tooltip is open.

PropTypeDefault
asenumdiv
aria-labelstringNo default value
portalledbooleantrue
sideenum"bottom"
sideOffsetnumber0
alignenum"center"
alignOffsetnumber0
avoidCollisionsbooleantrue
collisionToleranceboolean0

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.

PropTypeDefault
asenumsvg
widthnumber10
heightnumber5
offsetnumberNo default value

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.

const scaleIn = keyframes({
'0%': { opacity: 0, transform: 'scale(0)' },
'100%': { opacity: 1, transform: 'scale(1)' },
});
const StyledContent = styled(Tooltip.Content, {
transformOrigin: 'var(--radix-tooltip-content-transform-origin)',
animation: `${scaleIn} 0.5s ease-out`,
});

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.

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 StyledContent = styled(Tooltip.Content, {
'&[data-side="top"]': { animationName: slideUp },
'&[data-side="bottom"]': { animationName: slideDown },
animationDuration: '0.6s',
animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
});

A disabled button will not fire events so adding a tooltip to a disabled trigger requires a little more work.

Render the trigger as a wrapper (span for example) and place your disabled button inside with some CSS to disable pointer events. This allows the tooltip to receive all mouse and keyboard events instead of being blocked by the button.

import { styled } from 'path-to/stitches.config';
import * as Tooltip from '@radix-ui/react-tooltip';
import { BlendingModeIcon } from '@radix-ui/react-icons';
const StyledContent = styled(Tooltip.Content, {
borderRadius: 3,
padding: '5px 10px',
fontSize: 14,
backgroundColor: 'gainsboro',
color: 'black',
});
const StyledArrow = styled(Tooltip.Arrow, {
fill: 'gainsboro',
});
export default () => (
<Tooltip.Root>
<Tooltip.Trigger as="span">
<button type="button" disabled style={{ pointerEvents: 'none' }} >
<BlendingModeIcon />
</button>
</Tooltip.Trigger>
<StyledContent>
Blending mode
<StyledArrow />
</StyledContent>
</Tooltip.Root>
);

Use the delayDuration prop to control the time it takes for the tooltip to open. We recommend wrapping the tooltip in your own component with a default value for the prop for consistent timings across your app. You will still be able to override the prop per-instance.

import { styled } from 'path-to/stitches.config';
import * as Tooltip from '@radix-ui/react-tooltip';
import { BlendingModeIcon } from '@radix-ui/react-icons';
const StyledContent = styled(Tooltip.Content, {
borderRadius: 3,
padding: '5px 10px',
fontSize: 14,
backgroundColor: 'gainsboro',
color: 'black',
});
const StyledArrow = styled(Tooltip.Arrow, {
fill: 'gainsboro',
});
export default () => (
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>
<BlendingModeIcon />
</Tooltip.Trigger>
<StyledContent>
Blending mode
<StyledArrow />
</StyledContent>
</Tooltip.Root>
);
KeyDescription
TabOpens/closes the tooltip without delay.
SpaceIf open, closes the tooltip without delay.
EnterIf open, closes the tooltip without delay.
EscapeIf 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.

Usage

import { Tooltip } from './your-tooltip';
export default () => (
<Tooltip content="Tooltip content">
<button>Tooltip trigger</button>
</Tooltip>
);

Implementation

Use the Slot utility 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';
import { Slot } from '@radix-ui/react-slot';
export function Tooltip({ children, content, open, defaultOpen, onOpenChange, ...props }) {
return (
<TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange} >
<TooltipPrimitive.Trigger as={Slot}>
{children}
</TooltipPrimitive.Trigger>
<TooltipPrimitive.Content side="top" align="center" {...props}>
{content}
<TooltipPrimitive.Arrow offset={5} width={11} height={5} />
</TooltipPrimitive.Content>
</TooltipPrimitive.Root>
);
}