Jone

Tooltip

A high-quality, unstyled React tooltip component that appears when an element is hovered or focused, showing a hint for sighted users.

A popup that appears when an element is hovered or focused, showing a hint for sighted users.

Usage guidelines

  • Prefer using tooltips as visual labels only: Tooltips should act as supplementary visual labels for sighted mouse and keyboard users. Tooltips alone are not accessible to touch or screen reader users. See Alternatives to tooltips for more details.
  • Provide an accessible name for the trigger: Tooltips are visual-only elements and are not a replacement for labeling the trigger. The tooltip's trigger must have an aria-label attribute that closely matches the tooltip's content to ensure consistency for screen reader users.

Anatomy

Import the component and assemble its parts:

Anatomy
import { Tooltip } from '@base-ui/react/tooltip';

<Tooltip.Provider>
  <Tooltip.Root>
    <Tooltip.Trigger />
    <Tooltip.Portal>
      <Tooltip.Positioner>
        <Tooltip.Popup>
          <Tooltip.Arrow />
        </Tooltip.Popup>
      </Tooltip.Positioner>
    </Tooltip.Portal>
  </Tooltip.Root>
</Tooltip.Provider>;

Alternatives to tooltips

Tooltips should be supplementary popups that provide non-essential clarity in high-density UIs. A user should not miss critical information if they never see a tooltip.

Tooltips don't work well with touch input. Unlike mouse pointers with hover capability, there's no easily discoverable way to reveal a tooltip before tapping its trigger on a touch device.

iOS doesn't provide a system-standard, touch-friendly tooltip affordance, while Android may show a tooltip on long press. However, on the web, long press is often used to trigger contextual menus in the browser, which can lead to potential conflicts. For this reason, tooltips are disabled on touch devices.

Infotips

Popups that open when hovering an info icon should use Popover with the openOnHover prop on the trigger instead of a tooltip. This way, touch users and screen reader users can access the content.

To know when to reach for a popover instead of a tooltip, consider the purpose of the trigger element: If the trigger's purpose is to open the popup itself, it's a popover. If the trigger's purpose is unrelated to opening the popup, it's a tooltip.

Description text

Tooltips are designed for sighted users and are not a reliable way to deliver important information to touch users or assistive technologies. If the description is important to understanding the element, don't hide it behind a tooltip — use inline text or Popover if space is limited, so the information is accessible to everyone.

Since tooltips serve sighted mouse and keyboard users, iconography should clearly communicate the purpose of icon-only triggers, especially on mobile where the text label may not be visible.

If the description is not critical, a tooltip can still be used to provide extra clarity for sighted mouse or keyboard users.

Contextual feedback messages

Use the Toast component's anchoring ability for more ergonomic DX, to ensure the message is announced to screen readers, and to support complex content.

Examples

Detached triggers

A tooltip can be controlled by a trigger located either inside or outside the <Tooltip.Root> component. For simple, one-off interactions, place the <Tooltip.Trigger> inside <Tooltip.Root>, as shown in the example at the top of this page.

However, if defining the tooltip's content next to its trigger is not practical, you can use a detached trigger. This involves placing the <Tooltip.Trigger> outside of <Tooltip.Root> and linking them with a handle created by the Tooltip.createHandle() function.

Detached triggers
const demoTooltip = Tooltip.createHandle();

<Tooltip.Trigger handle={demoTooltip}>Button</Tooltip.Trigger>

<Tooltip.Root handle={demoTooltip}>
  ...
</Tooltip.Root>

Multiple triggers

A single tooltip can be opened by multiple trigger elements. You can achieve this by using the same handle for several detached triggers, or by placing multiple <Tooltip.Trigger> components inside a single <Tooltip.Root>.

Multiple triggers within the Root part
<Tooltip.Root>
  <Tooltip.Trigger>Trigger 1</Tooltip.Trigger>
  <Tooltip.Trigger>Trigger 2</Tooltip.Trigger>
  ...
</Tooltip.Root>
Multiple detached triggers
const demoTooltip = Tooltip.createHandle();

<Tooltip.Trigger handle={demoTooltip}>
  Trigger 1
</Tooltip.Trigger>

<Tooltip.Trigger handle={demoTooltip}>
  Trigger 2
</Tooltip.Trigger>

<Tooltip.Root handle={demoTooltip}>
  ...
</Tooltip.Root>

The tooltip can render different content depending on which trigger opened it. This is achieved by passing a payload to the <Tooltip.Trigger> and using the function-as-a-child pattern in <Tooltip.Root>.

The payload can be strongly typed by providing a type argument to the createHandle() function:

Detached triggers with payload
const demoTooltip = Tooltip.createHandle<{ text: string }>();

<Tooltip.Trigger handle={demoTooltip} payload={{ text: 'Trigger 1' }}>
  Trigger 1
</Tooltip.Trigger>

<Tooltip.Trigger handle={demoTooltip} payload={{ text: 'Trigger 2' }}>
  Trigger 2
</Tooltip.Trigger>

<Tooltip.Root handle={demoTooltip}>
  {({ payload }) => (
    <Tooltip.Portal>
      <Tooltip.Positioner sideOffset={8}>
        <Tooltip.Popup className={styles.Popup}>
          <Tooltip.Arrow className={styles.Arrow}>
            <ArrowSvg />
          </Tooltip.Arrow>
          {payload !== undefined && (
            <span>
              Tooltip opened by {payload.text}
            </span>
          )}
        </Tooltip.Popup>
      </Tooltip.Positioner>
    </Tooltip.Portal>
  )}
</Tooltip.Root>

Controlled mode with multiple triggers

You can control the tooltip's open state externally using the open and onOpenChange props on <Tooltip.Root>. This allows you to manage the tooltip's visibility based on your application's state. When using multiple triggers, you have to manage which trigger is active with the triggerId prop on <Tooltip.Root> and the id prop on each <Tooltip.Trigger>.

Note that there is no separate onTriggerIdChange prop. Instead, the onOpenChange callback receives an additional argument, eventDetails, which contains the trigger element that initiated the state change.

Animating the Tooltip

You can animate a tooltip as it moves between different trigger elements. This includes animating its position, size, and content.

Position and Size

To animate the tooltip's position, apply CSS transitions to the left, right, top, and bottom properties of the Positioner part. To animate its size, transition the width and height of the Popup part.

Content

The tooltip also supports content transitions. This is useful when different triggers display different content within the same tooltip.

To enable content animations, wrap the content in the <Tooltip.Viewport> part. This part provides features to create direction-aware animations. It renders a div with a data-activation-direction attribute (left, right, up, or down) that indicates the new trigger's position relative to the previous one.

Inside the <Tooltip.Viewport>, the content is further wrapped in divs with data attributes to help with styling:

  • data-current: The currently visible content when no transitions are present or the incoming content.
  • data-previous: The outgoing content during a transition.

You can use these attributes to style the enter and exit animations.

API reference

Provider

NameTypeDefaultDescription
childrenOptionalReact.ReactNode
delayOptionalnumber | undefinedHow long to wait before opening a tooltip. Specified in milliseconds.
closeDelayOptionalnumber | undefinedHow long to wait before closing a tooltip. Specified in milliseconds.
timeoutOptionalnumber | undefined400Another tooltip will open instantly if the previous tooltip is closed within this timeout. Specified in milliseconds.

Root

NameTypeDefaultDescription
defaultOpenOptionalboolean | undefinedfalseWhether the tooltip is initially open. To render a controlled tooltip, use the `open` prop instead.
openOptionalboolean | undefinedWhether the tooltip is currently open.
onOpenChangeCompleteOptional((open: boolean) => void) | undefinedEvent handler called after any animations complete when the tooltip is opened or closed.
disableHoverablePopupOptionalboolean | undefinedfalseWhether the tooltip contents can be hovered without closing the tooltip.
trackCursorAxisOptional'none' | 'x' | 'y' | 'both' | undefinednoneDetermines which axis the tooltip should track the cursor on.
actionsRefOptionalReact.RefObject<TooltipRoot.Actions | null> | undefinedA ref to imperative actions. - `unmount`: Unmounts the tooltip popup. - `close`: Closes the tooltip imperatively when called.
disabledOptionalboolean | undefinedfalseWhether the tooltip is disabled.
handleOptionalTooltipHandle<Payload> | undefinedA handle to associate the tooltip with a trigger. If specified, allows external triggers to control the tooltip's open state. Can be created with the Tooltip.createHandle() method.
childrenOptionalReact.ReactNode | PayloadChildRenderFunction<Payload>The content of the tooltip. This can be a regular React node or a render function that receives the `payload` of the active trigger.
triggerIdOptionalstring | null | undefinedID of the trigger that the tooltip is associated with. This is useful in conjunction with the `open` prop to create a controlled tooltip. There's no need to specify this prop when the tooltip is uncontrolled (i.e. when the `open` prop is not set).
defaultTriggerIdOptionalstring | null | undefinedID of the trigger that the tooltip is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open tooltip.

Trigger

NameTypeDefaultDescription
handleOptionalTooltipHandle<Payload> | undefinedA handle to associate the trigger with a tooltip.
payloadOptionalPayload | undefinedA payload to pass to the tooltip when it is opened.
delayOptionalnumber | undefined600How long to wait before opening the tooltip. Specified in milliseconds.
closeOnClickOptionalboolean | undefinedtrueWhether the tooltip should close when this trigger is clicked.
closeDelayOptionalnumber | undefined0How long to wait before closing the tooltip. Specified in milliseconds.
disabledOptionalboolean | undefinedfalseIf `true`, the tooltip will not open when interacting with this trigger. Note that this doesn't apply the `disabled` attribute to the trigger element. If you want to disable the trigger element itself, you can pass the `disabled` prop to the trigger element via the `render` prop.

Portal

NameTypeDefaultDescription
keepMountedOptionalboolean | undefinedfalseWhether to keep the portal mounted in the DOM while the popup is hidden.

Positioner

NameTypeDefaultDescription
sideOptionalSide | undefinedtopWhich side of the anchor element to align the popup against. May automatically change to avoid collisions.

On this page