useTooltipTrigger
Provides the behavior and accessibility implementation for a tooltip trigger, e.g. a button that shows a description when focused or hovered.
install | yarn add react-aria |
---|---|
version | 3.29.1 |
usage | import {useTooltipTrigger, useTooltip} from 'react-aria' |
API#
useTooltipTrigger(
props: TooltipTriggerProps,
state: TooltipTriggerState,
ref: RefObject<FocusableElement>
): TooltipTriggerAria
useTooltip(
(props: AriaTooltipProps,
, state?: TooltipTriggerState
)): TooltipAria
Features#
The HTML title
attribute can be used to create a tooltip, but it cannot be styled. Custom styled
tooltips can be hard to build in an accessible way. useTooltipTrigger
and useTooltip
help build
fully accessible tooltips that can be styled as needed.
- Keyboard focus management and cross browser normalization
- Hover management and cross browser normalization
- Labeling support for screen readers (aria-describedby)
- Exposed as a tooltip to assistive technology via ARIA
- Matches native tooltip behavior with delay on hover of first tooltip and no delay on subsequent tooltips.
Anatomy#
A tooltip consists of two parts: the trigger element and the tooltip itself. Users may reveal the tooltip by hovering or focusing the trigger.
useTooltipTrigger
returns props to be spread onto its target trigger and the tooltip:
Name | Type | Description |
triggerProps | DOMAttributes
& PressProps
& HoverProps
& FocusEvents | Props for the trigger element. |
tooltipProps | DOMAttributes | Props for the overlay container element. |
useTooltip
returns props to be spread onto the tooltip:
Name | Type | Description |
tooltipProps | DOMAttributes | Props for the tooltip element. |
Tooltip state is managed by the useTooltipTriggerState
hook in @react-stately/tooltip
. The state object should be passed as an option to useTooltipTrigger
and useTooltip
.
Example#
This example implements a Tooltip that renders in an absolute position next to its target. The useTooltip
hook applies the correct ARIA attributes to the tooltip, and useTooltipTrigger
associates it to the trigger element.
Two instances of the example are rendered to demonstrate the behavior of the delay on hover. If you hover over the first button, the tooltip will be shown after a delay. Hovering over the second button shows the tooltip immediately. If you wait for a delay before hovering another element, the delay restarts.
import {useTooltipTriggerState} from 'react-stately';
import {mergeProps, useTooltip, useTooltipTrigger} from 'react-aria';
function Tooltip({ state, ...props }) {
let { tooltipProps } = useTooltip(props, state);
return (
<span
style={{
position: 'absolute',
left: '5px',
top: '100%',
maxWidth: 150,
marginTop: '10px',
backgroundColor: 'white',
color: 'black',
padding: '5px',
border: '1px solid gray'
}}
{...mergeProps(props, tooltipProps)}
>
{props.children}
</span>
);
}
function TooltipButton(props) {
let state = useTooltipTriggerState(props);
let ref = React.useRef(null);
// Get props for the trigger and its tooltip
let { triggerProps, tooltipProps } = useTooltipTrigger(props, state, ref);
return (
<span style={{ position: 'relative' }}>
<button
ref={ref}
{...triggerProps}
style={{ fontSize: 18 }}
onClick={() => alert('Pressed button')}
>
{props.children}
</button>
{state.isOpen && (
<Tooltip state={state} {...tooltipProps}>{props.tooltip}</Tooltip>
)}
</span>
);
}
<TooltipButton tooltip="Edit">✏️</TooltipButton>
<TooltipButton tooltip="Delete">🚮</TooltipButton>
import {useTooltipTriggerState} from 'react-stately';
import {
mergeProps,
useTooltip,
useTooltipTrigger
} from 'react-aria';
function Tooltip({ state, ...props }) {
let { tooltipProps } = useTooltip(props, state);
return (
<span
style={{
position: 'absolute',
left: '5px',
top: '100%',
maxWidth: 150,
marginTop: '10px',
backgroundColor: 'white',
color: 'black',
padding: '5px',
border: '1px solid gray'
}}
{...mergeProps(props, tooltipProps)}
>
{props.children}
</span>
);
}
function TooltipButton(props) {
let state = useTooltipTriggerState(props);
let ref = React.useRef(null);
// Get props for the trigger and its tooltip
let { triggerProps, tooltipProps } = useTooltipTrigger(
props,
state,
ref
);
return (
<span style={{ position: 'relative' }}>
<button
ref={ref}
{...triggerProps}
style={{ fontSize: 18 }}
onClick={() => alert('Pressed button')}
>
{props.children}
</button>
{state.isOpen && (
<Tooltip state={state} {...tooltipProps}>
{props.tooltip}
</Tooltip>
)}
</span>
);
}
<TooltipButton tooltip="Edit">✏️</TooltipButton>
<TooltipButton tooltip="Delete">🚮</TooltipButton>
import {useTooltipTriggerState} from 'react-stately';
import {
mergeProps,
useTooltip,
useTooltipTrigger
} from 'react-aria';
function Tooltip(
{ state, ...props }
) {
let { tooltipProps } =
useTooltip(
props,
state
);
return (
<span
style={{
position:
'absolute',
left: '5px',
top: '100%',
maxWidth: 150,
marginTop:
'10px',
backgroundColor:
'white',
color: 'black',
padding: '5px',
border:
'1px solid gray'
}}
{...mergeProps(
props,
tooltipProps
)}
>
{props.children}
</span>
);
}
function TooltipButton(
props
) {
let state =
useTooltipTriggerState(
props
);
let ref = React.useRef(
null
);
// Get props for the trigger and its tooltip
let {
triggerProps,
tooltipProps
} = useTooltipTrigger(
props,
state,
ref
);
return (
<span
style={{
position:
'relative'
}}
>
<button
ref={ref}
{...triggerProps}
style={{
fontSize: 18
}}
onClick={() =>
alert(
'Pressed button'
)}
>
{props.children}
</button>
{state.isOpen && (
<Tooltip
state={state}
{...tooltipProps}
>
{props.tooltip}
</Tooltip>
)}
</span>
);
}
<TooltipButton tooltip="Edit">
✏️
</TooltipButton>
<TooltipButton tooltip="Delete">
🚮
</TooltipButton>
Usage#
The following examples show how to use the TooltipButton
component created in the above example.
Delay#
Tooltips appear after a short delay when hovering the trigger, or instantly when using keyboard focus. This delay can be adjusted for hover using the delay
prop.
<TooltipButton tooltip="Save" delay={0}>💾</TooltipButton>
<TooltipButton tooltip="Save" delay={0}>💾</TooltipButton>
<TooltipButton
tooltip="Save"
delay={0}
>
💾
</TooltipButton>
Close Delay#
Tooltips disappear after a short delay when no longer hovering the trigger, or instantly when using keyboard focus. This delay can be adjusted for hover using the closeDelay
prop.
<TooltipButton tooltip="Refresh" closeDelay={0}>🔄</TooltipButton>
<TooltipButton tooltip="Refresh" closeDelay={0}>
🔄
</TooltipButton>
<TooltipButton
tooltip="Refresh"
closeDelay={0}
>
🔄
</TooltipButton>
Trigger#
By default, tooltips appear both on hover and on focus. The trigger
prop can be set to "focus"
to display the tooltip only on focus, and not on hover.
<TooltipButton tooltip="Burn CD" trigger="focus">💿</TooltipButton>
<TooltipButton tooltip="Burn CD" trigger="focus">
💿
</TooltipButton>
<TooltipButton
tooltip="Burn CD"
trigger="focus"
>
💿
</TooltipButton>
Controlled open state#
The open state of the tooltip can be controlled via the defaultOpen
and isOpen
props.
function Example() {
let [isOpen, setOpen] = React.useState(false);
return (
<>
<p>Tooltip is {isOpen ? 'showing' : 'not showing'}</p>
<TooltipButton
tooltip="Notifications"
isOpen={isOpen}
onOpenChange={setOpen}
>
📣
</TooltipButton>
</>
);
}
function Example() {
let [isOpen, setOpen] = React.useState(false);
return (
<>
<p>Tooltip is {isOpen ? 'showing' : 'not showing'}</p>
<TooltipButton
tooltip="Notifications"
isOpen={isOpen}
onOpenChange={setOpen}
>
📣
</TooltipButton>
</>
);
}
function Example() {
let [isOpen, setOpen] =
React.useState(
false
);
return (
<>
<p>
Tooltip is{' '}
{isOpen
? 'showing'
: 'not showing'}
</p>
<TooltipButton
tooltip="Notifications"
isOpen={isOpen}
onOpenChange={setOpen}
>
📣
</TooltipButton>
</>
);
}
Disabled#
The isDisabled
prop can be provided to a TooltipTrigger to disable the tooltip, without disabling the trigger it displays on.
<TooltipButton tooltip="Print" isDisabled>🖨</TooltipButton>
<TooltipButton tooltip="Print" isDisabled>🖨</TooltipButton>
<TooltipButton
tooltip="Print"
isDisabled
>
🖨
</TooltipButton>
Internationalization#
RTL#
In right-to-left languages, tooltips should be mirrored across trigger. Ensure that your CSS and overlay positioning (if any) accounts for this.