<div className="mt-6">
  <img
    className="select-none"
    src="/tooltip.svg"
    alt="Tooltip"
    width={692}
    height={212}
    draggable={false}
  />
</div>

A [tooltip](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/) is
a floating element that displays information related to an anchor
element when it receives keyboard focus or the mouse hovers over
it.

## Essentials

An accessible tooltip component has the following qualities:

- **Dynamic anchor positioning**: The tooltip is positioned next
  to its reference element, and remains anchored to it while
  avoiding collisions.
- **Events**: When the mouse hovers over the reference element,
  or when the reference element receives keyboard focus, the
  tooltip opens. When the mouse leaves, or the reference is
  blurred, the tooltip closes.
- **Dismissal**: When the user presses the `esc` key while the
  tooltip is open, it closes.
- **Role**: The elements are given relevant role and ARIA
  attributes to be accessible to screen readers.

## Examples

Both of these examples have sections explaining them in-depth
below.

- [Basic tooltip](https://codesandbox.io/s/cranky-river-ifx8s9?file=/src/App.tsx)
- [Reusable tooltip component](https://codesandbox.io/s/xenodochial-grass-js3bo9?file=/src/App.tsx)

## Basic tooltip

[CodeSandbox demo](https://codesandbox.io/s/cranky-river-ifx8s9?file=/src/App.tsx)

This example demonstrates how to create a tooltip for use in a
single instance to familiarize yourself with the fundamentals.

Let's walk through the example:

### Open state

```js {4}
import {useState} from 'react';

function Tooltip() {
  const [isOpen, setIsOpen] = useState(false);
}
```

`isOpen{:.const}` determines whether or not the tooltip is
currently open on the screen. It is used for conditional
rendering.

### useFloating hook

The `useFloating(){:js}` hook provides positioning and context
for our tooltip. We need to pass it some information:

- `open{:.objectKey}`: The open state from our `useState(){:js}`
  hook above.
- `onOpenChange{:.objectKey}`: A callback function that will be
  called when the tooltip is opened or closed. We'll use this to
  update our `isOpen{:.const}` state.
- `middleware{:.objectKey}`: Import and pass middleware to the
  array that ensure the tooltip remains on the screen, no matter
  where it ends up being positioned.
- `whileElementsMounted{:.objectKey}`: Ensure the tooltip remains
  anchored to the reference element by updating the position when
  necessary, only while both the reference and floating elements
  are mounted for performance.

```js /useFloating/
import {
  useFloating,
  autoUpdate,
  offset,
  flip,
  shift,
} from '@floating-ui/react';

function Tooltip() {
  const [isOpen, setIsOpen] = useState(false);

  const {refs, floatingStyles, context} = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [offset(10), flip(), shift()],
    whileElementsMounted: autoUpdate,
  });
}
```

### Interaction hooks

Interaction hooks return objects containing keys of props that
enable the tooltip to be opened, closed, or accessible to screen
readers.

Using the `context{:.const}` that was returned from the hook,
call the interaction hooks:

```js /context/
import {
  // ...
  useHover,
  useFocus,
  useDismiss,
  useRole,
  useInteractions,
} from '@floating-ui/react';

function Tooltip() {
  const [isOpen, setIsOpen] = useState(false);

  const {refs, floatingStyles, context} = useFloating({
    open: isOpen,
    onOpenChange: setIsOpen,
    middleware: [offset(10), flip(), shift()],
    whileElementsMounted: autoUpdate,
  });

  const hover = useHover(context, {move: false});
  const focus = useFocus(context);
  const dismiss = useDismiss(context);
  const role = useRole(context, {role: 'tooltip'});

  // Merge all the interactions into prop getters
  const {getReferenceProps, getFloatingProps} = useInteractions([
    hover,
    focus,
    dismiss,
    role,
  ]);
}
```

- `useHover(){:js}` adds the ability to toggle the tooltip open
  or closed when the reference element is hovered over. The
  `move{:.objectKey}` option is set to false so that
  `mousemove{:.string}` events are ignored.
- `useFocus(){:js}` adds the ability to toggle the tooltip open
  or closed when the reference element is focused.
- `useDismiss(){:js}` adds the ability to dismiss the tooltip
  when the user presses the `esc` key.
- `useRole(){:js}` adds the correct ARIA attributes for a
  `tooltip{:.string}` to the tooltip and reference elements.

Finally, `useInteractions(){:js}` merges all of their props into
prop getters which can be used for rendering.

### Rendering

Now we have all the variables and hooks set up, we can render out
our elements.

```js
function Tooltip() {
  // ...
  return (
    <>
      <button ref={refs.setReference} {...getReferenceProps()}>
        Reference element
      </button>
      {isOpen && (
        <div
          ref={refs.setFloating}
          style={floatingStyles}
          {...getFloatingProps()}
        >
          Tooltip element
        </div>
      )}
    </>
  );
}
```

- `{...getReferenceProps()}{:js}` /
  `{...getFloatingProps()}{:js}` spreads the props from the
  interaction hooks onto the relevant elements. They contain
  props like `onMouseEnter{:.keyword}`,
  `aria-describedby{:.keyword}`, etc.

## Reusable tooltip component

[CodeSandbox demo](https://codesandbox.io/s/xenodochial-grass-js3bo9?file=/src/App.tsx)

It is better to create a reusable component API that can be used
in a variety of different scenarios more easily. We can place all
of our hooks into a single custom hook for better reusability,
which is then used by a controller component which encapsulates
the state.

The reusable component can:

- Be uncontrolled or controlled
- Accept any element as the `<TooltipTrigger />{:js}`
- Read the open state to change styles

```js
function App() {
  return (
    <Tooltip>
      <TooltipTrigger>My trigger</TooltipTrigger>
      <TooltipContent>My tooltip</TooltipContent>
    </Tooltip>
  );
}
```

### Controller component

- `<Tooltip />{:js}`

This is the controller component that manages the tooltip's state
and provides the API to the rest of the components.

### Render components

These components read the context provided by the root Tooltip
component and render the appropriate elements.

The components must be wrapped in `forwardRef(){:js}` to allow
refs, and should merge the refs to ensure all refs are preserved
and forwarded to the element. Props are also merged to prevent
overwriting.

- `<TooltipTrigger />{:js}` is the trigger button the tooltip is
  attached to. This accepts an `asChild{:.keyword}` prop if you
  want to attach it to a custom element. It also has a
  `data-state{:.keyword}` attached to style based on the
  open/closed state.
- `<TooltipContent />{:js}` is the tooltip element, which can
  contain any children (React nodes).

<Notice type="warning" gap="above">
  Tooltips should not contain interactive content. Use a Popover
  instead!
</Notice>

## Delay groups

One of the most useful UX improvements for tooltips is making
nearby tooltips share a delay.

```js
<FloatingDelayGroup delay={200}>
  <Tooltip>
    <TooltipTrigger>Ref 1</TooltipTrigger>
    <TooltipContent className="Tooltip">Label 1</TooltipContent>
  </Tooltip>
  <Tooltip>
    <TooltipTrigger>Ref 2</TooltipTrigger>
    <TooltipContent className="Tooltip">Label 2</TooltipContent>
  </Tooltip>
  <Tooltip>
    <TooltipTrigger>Ref 3</TooltipTrigger>
    <TooltipContent className="Tooltip">Label 3</TooltipContent>
  </Tooltip>
</FloatingDelayGroup>
```

- [CodeSandbox demo](https://codesandbox.io/s/clever-aryabhata-yq3xyc?file=/src/Group.tsx)
- [`FloatingDelayGroup` reference](/docs/FloatingDelayGroup)

## Disabled buttons

Sometimes you want to disable a button, but still show the
tooltip while it's disabled.

[Disabling a button with a tooltip prevents it from being accessible](/docs/react#disabled-elements),
but can be worked around using a different prop. This supplants
the `disabled{:.keyword}` prop to allow events to fire, including
keyboard access.

```js
const Button = React.forwardRef(function Button(
  {visuallyDisabled, disabled, ...props},
  ref
) {
  return (
    <button
      {...props}
      ref={ref}
      disabled={visuallyDisabled ? undefined : disabled}
      aria-disabled={visuallyDisabled ? 'true' : undefined}
      // You'll want to do this for all relevant event handlers.
      onClick={visuallyDisabled ? undefined : props.onClick}
    />
  );
});
```
