Overlays & Feedback

Tooltip

An anchored React tooltip that opens on hover or keyboard focus after a delay, switches instantly while moving within a group, fades and scales in, flips at the viewport edge, and carries an arrow pointing at its trigger.

Add a one-line hint to icon buttons, abbreviations or anything that needs a short explanation. It opens after the pointer rests past the delay (700ms by default) and immediately on keyboard Tab focus; tooltips wrapped in a Tooltip.Provider share their timing — sweep along a toolbar and the next tooltip skips the delay and switches straight over. The bubble is portalled to body and positioned with position: fixed; when there is not enough room it flips to the opposite side and shifts along the cross axis to stay in the viewport, with the arrow always pointing at the trigger's centre. On touch devices it opens on long-press.

Bring this into your project

Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.

npx webberui@latest add tooltip.en

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

700
100
Active edited usageDefault props
<Tooltip/>

The complete example includes the preview layout and data. Install the component first, then paste this file into your project.

Installation

npx webberui@latest add https://webberui.com/r/tooltip.en.json

Or, once registries are configured in components.json, install it as @webberui/tooltip.

Usage

import { Tooltip } from "@/components/ui/tooltip";

// A single tooltip
<Tooltip content="Copy link">
  <button type="button" aria-label="Copy link">…</button>
</Tooltip>

// Toolbar: fast switching within one Provider shows instantly
<Tooltip.Provider delay={700} skipDelay={300}>
  <Tooltip content="Bold ⌘B">
    <button type="button" aria-label="Bold">B</button>
  </Tooltip>
  <Tooltip content="Italic ⌘I" side="bottom">
    <button type="button" aria-label="Italic">I</button>
  </Tooltip>
</Tooltip.Provider>

Props

PropTypeDefaultDescription
contentReactNode—Tooltip content
childrenReactElement—A single focusable trigger; events, the ref and aria-describedby are merged in via cloneElement
side"top" | "right" | "bottom" | "left""top"Preferred side; flips to the opposite side when there is not enough room
align"start" | "center" | "end""center"Cross-axis alignment
sideOffsetnumber4Distance from the trigger (px)
alignOffsetnumber0Cross-axis offset (px)
collisionPaddingnumber8Minimum distance kept from the viewport edge (px)
delaynumber700Open delay (ms); when unset, uses the delay of Tooltip.Provider
closeDelaynumber0Close delay after the mouse leaves (ms)
durationnumber100Entry/exit animation duration (ms), also written to --wb-tooltip-duration
openboolean—Controlled open state
defaultOpenbooleanfalseInitial open state in uncontrolled mode
onOpenChange(open: boolean) => void—Called when the open state changes
disabledbooleanfalseDisabled: never opens, and closes if already open
showArrowbooleantrueWhether to show the arrow pointing at the trigger
classNamestring—Merged into the floating root (the dark bubble itself)

Tooltip.Provider

FieldTypeDefaultDescription
delaynumber700Default open delay for tooltips in the group (ms)
skipDelaynumber300Within this window (ms) after any tooltip closes, moving to another trigger in the group opens it instantly with no delay
childrenReactNode—Group content

How it works

  • Open/close rules: pointer enter starts the delay timer, pointer leave cancels it or closes after closeDelay; pressing the trigger with a mouse dismisses the tooltip; keyboard focus (:focus-visible) opens immediately and blur closes; Escape closes; scrolling the container that holds the trigger closes.
  • Touch: a 500ms long-press opens it and it closes 1500ms after the finger lifts; the click that follows the long-press is swallowed so the button is not triggered by accident.
  • Positioning: the bubble stays visibility: hidden until first measured, so it never flashes in the top-left corner; size changes of the trigger or bubble, window resizes and scrolling of any container recompute it, throttled with rAF. The bubble carries data-side / data-align (the actual placement after flipping) for custom styling.
  • Animation: it fades in, scales from 0.95 to 1 and slides 8px out from the trigger's side; transform-origin follows the actual placement so the scale grows out of the corner nearest the trigger. To retune the timing globally, override --wb-tooltip-duration in CSS.
  • The tooltip itself is pointer-events: none and not interactive; for content with links or buttons, use a popover-style component instead.

Accessibility

  • The bubble has role="tooltip" with an id from useId; while open, the trigger gets aria-describedby pointing at it (any existing aria-describedby is kept)
  • Keyboard Tab focus opens it and Escape closes it; focus caused by a mouse click does not open it, so the tooltip does not stick around
  • Icon-only trigger buttons should still carry their own aria-label — the tooltip is supplementary, not a replacement for the accessible name
  • When the user's system has "reduce motion" enabled, only the fade remains (same duration), with no scaling or sliding