useTooltipTrigger

Provides the behavior and accessibility implementation for a tooltip trigger, e.g. a button that shows a description when focused or hovered.

installyarn add react-aria
version3.32.0
usageimport {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#


FlipTooltipTooltipTrigger

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:

NameTypeDescription
triggerPropsDOMAttributesProps for the trigger element.
tooltipPropsDOMAttributesProps for the overlay container element.

useTooltip returns props to be spread onto the tooltip:

NameTypeDescription
tooltipPropsDOMAttributesProps 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.