Skip to main content

Tooltip

A hover/focus hint with delay, skip-delay, and grace area support.

import * as Tooltip from '@gentleduck/primitives/tooltip'
import * as Tooltip from '@gentleduck/primitives/tooltip'

Anatomy

<Tooltip.Provider>
  <Tooltip.Root>
    <Tooltip.Trigger />
    <Tooltip.Portal>
      <Tooltip.Content>
        <Tooltip.Arrow />
      </Tooltip.Content>
    </Tooltip.Portal>
  </Tooltip.Root>
</Tooltip.Provider>
<Tooltip.Provider>
  <Tooltip.Root>
    <Tooltip.Trigger />
    <Tooltip.Portal>
      <Tooltip.Content>
        <Tooltip.Arrow />
      </Tooltip.Content>
    </Tooltip.Portal>
  </Tooltip.Root>
</Tooltip.Provider>

Example

import * as Tooltip from '@gentleduck/primitives/tooltip'
 
function App() {
  return (
    <Tooltip.Provider delayDuration={400}>
      <Tooltip.Root>
        <Tooltip.Trigger className="px-3 py-1 border rounded">
          Hover me
        </Tooltip.Trigger>
        <Tooltip.Portal>
          <Tooltip.Content
            className="bg-gray-900 text-white text-sm px-3 py-1.5 rounded shadow"
            sideOffset={5}
          >
            This is a tooltip
            <Tooltip.Arrow className="fill-gray-900" />
          </Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
    </Tooltip.Provider>
  )
}
import * as Tooltip from '@gentleduck/primitives/tooltip'
 
function App() {
  return (
    <Tooltip.Provider delayDuration={400}>
      <Tooltip.Root>
        <Tooltip.Trigger className="px-3 py-1 border rounded">
          Hover me
        </Tooltip.Trigger>
        <Tooltip.Portal>
          <Tooltip.Content
            className="bg-gray-900 text-white text-sm px-3 py-1.5 rounded shadow"
            sideOffset={5}
          >
            This is a tooltip
            <Tooltip.Arrow className="fill-gray-900" />
          </Tooltip.Content>
        </Tooltip.Portal>
      </Tooltip.Root>
    </Tooltip.Provider>
  )
}

API

Tooltip.Provider

Wraps your app (or section) to coordinate tooltip delays. When a user moves from one tooltip trigger to another quickly, the second tooltip opens instantly (skip delay).

PropTypeDefaultDescription
delayDurationnumber700Delay in ms before tooltip opens
skipDelayDurationnumber300Time in ms before delay resets after closing
disableHoverableContentbooleanfalseClose immediately when pointer leaves trigger (can't hover content)

Tooltip.Root

Manages state for a single tooltip instance.

PropTypeDefaultDescription
openboolean--Controlled open state
defaultOpenbooleanfalseInitial open state
onOpenChange(open: boolean) => void--Called on state change
delayDurationnumber--Override Provider's delay for this tooltip
disableHoverableContentboolean--Override Provider's setting

Tooltip.Trigger

The element that activates the tooltip on hover/focus.

Tooltip.Portal

Portals content to document.body.

Tooltip.Content

The floating tooltip content. Positioned by the Popper engine.

PropTypeDefaultDescription
aria-labelstring--Accessible label when content is purely visual
side'top' | 'right' | 'bottom' | 'left''top'Preferred side
sideOffsetnumber0Distance from anchor in pixels
align'start' | 'center' | 'end''center'Alignment along the side
alignOffsetnumber0Alignment offset in pixels
forceMounttrue--Keep mounted always
onEscapeKeyDown(event) => void--Called on Escape press
onPointerDownOutside(event) => void--Called on outside click

Tooltip.Arrow

Optional visual arrow pointing toward the trigger.


Data attributes


Grace area

When the user moves their pointer from the trigger toward the content, a grace area (triangle) keeps the tooltip open during the transition. This prevents the tooltip from closing when the user moves diagonally to interact with the content.

Set disableHoverableContent={true} to disable this behavior.


Keyboard interactions

KeyAction
TabFocus trigger, opens tooltip
EscapeClose tooltip