WebberUI

Cursor Tooltip

An elastic tooltip that follows the cursor, tracking its coordinates with a spring and flipping sides automatically near the edges, with keyboard focus anchoring.

Loading preview…
npx shadcn@latest add https://webberui.com/r/cursor-tooltip.json

Playground

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

14
0
<CursorTooltip />

Installation

npx shadcn@latest add https://webberui.com/r/cursor-tooltip.json

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

Usage

import { CursorTooltip } from "@/components/ui/cursor-tooltip";

<CursorTooltip content="Drag to reorder">
  <button>Hover me</button>
</CursorTooltip>

The tooltip is rendered into document.body through a portal and follows the cursor; content accepts any node, so you can build a rich tooltip with an icon:

<CursorTooltip
  variant="smooth"
  content={
    <span className="flex items-center gap-1.5">
      <Sparkles className="size-3.5" /> Done
    </span>
  }
>
  <StatusDot />
</CursorTooltip>

Props

PropTypeDefaultDescription
childrenReact.ReactNodeThe trigger area; the tooltip shows when the cursor enters it or it receives keyboard focus
contentReact.ReactNodeTooltip content, either text or any node
variant"spring" | "smooth" | "instant""spring"Follow feel: elastic spring back / tight tracking / zero-latency lock to the cursor
offsetnumber | { x: number; y: number }14Tooltip offset relative to the cursor (px)
openDelaynumber0Delay before showing (milliseconds)
disabledbooleanfalseDisables the tooltip: the trigger area renders as usual, but no tooltip appears
classNamestringAppended to the tooltip bubble class
wrapperClassNamestringAppended to the trigger wrapper class

How it works

  • Coordinates are written straight into useMotionValue, so mousemove never triggers a React re-render; useSpring makes the tooltip track the cursor with a spring, and low damping is what produces the "elastic" spring back.
  • spring has a pronounced spring back, smooth is tight and barely overshoots, and instant binds directly to the raw coordinates for a zero-latency lock.
  • On first show, the spring jumps to the cursor position rather than flying in from a corner of the screen.
  • As the tooltip approaches the right or bottom edge of the viewport it flips to the other side of the cursor automatically and is clamped inside the bounds, so it is never clipped or left covering the cursor.

Accessibility

  • The trigger area supports the keyboard through onFocus / onBlur: on focus the tooltip anchors directly above the trigger area (dropping below it when there is not enough room) instead of at the cursor position.
  • With a single child, aria-describedby is attached to the child itself so assistive technology reads the tooltip content on focus; the tooltip carries role="tooltip".
  • Pressing Esc closes the tooltip immediately.
  • When the user has "reduce motion" enabled at the system level, the spring and the scaling are disabled, degrading to a plain fade in / fade out locked to the cursor.
  • The tooltip is pointer-events-none, so it never intercepts pointer events.

On this page