Skip to main content

tooltip

A customizable and accessible tooltip component with Floating UI positioning, state-aware styling hooks, and flexible triggers.

Features

  • Floating UI powered positioning - Smart placement with flip, shift, and offset middleware.
  • State-aware styling hooks - data-state and data-side attributes for state and placement styling.
  • Transform-origin variable - Uses --gentleduck-tooltip-content-transform-origin for direction-aware animations.
  • Customizable delays - Configure open delays globally with TooltipProvider or per tooltip with delayDuration.
  • Flexible triggers - Wrap any element using asChild.
  • Accessible by default - Implements proper ARIA attributes and keyboard navigation.
  • Portal rendering - Renders tooltips in a portal to avoid layout clipping.

Philosophy

Tooltips are the lightest touch of contextual help — they appear on hover, require no interaction, and disappear when attention moves on. We build on Floating UI because positioning against viewport edges, scroll containers, and dynamic layouts is harder than it looks. The data-state and data-side attributes give you animation hooks without JavaScript state management.

How It's Built

Loading diagram...

Installation


npx @gentleduck/cli add tooltip

npx @gentleduck/cli add tooltip

Usage

import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from '@/components/ui/tooltip'
 
// app/layout.tsx (once)
<TooltipProvider>{children}</TooltipProvider>
 
<Tooltip delayDuration={500}>
  <TooltipTrigger>Hover</TooltipTrigger>
  <TooltipContent>Tooltip text</TooltipContent>
</Tooltip>
import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from '@/components/ui/tooltip'
 
// app/layout.tsx (once)
<TooltipProvider>{children}</TooltipProvider>
 
<Tooltip delayDuration={500}>
  <TooltipTrigger>Hover</TooltipTrigger>
  <TooltipContent>Tooltip text</TooltipContent>
</Tooltip>

Examples

Basic

Custom Trigger with asChild

<Tooltip>
  <TooltipTrigger asChild>
    <span className="cursor-pointer underline">Hover me</span>
  </TooltipTrigger>
  <TooltipContent>Custom element trigger</TooltipContent>
</Tooltip>
<Tooltip>
  <TooltipTrigger asChild>
    <span className="cursor-pointer underline">Hover me</span>
  </TooltipTrigger>
  <TooltipContent>Custom element trigger</TooltipContent>
</Tooltip>

Animated Tooltip

<Tooltip>
  <TooltipTrigger>Hover</TooltipTrigger>
  <TooltipContent className="TooltipContent">Animated tooltip</TooltipContent>
</Tooltip>
<Tooltip>
  <TooltipTrigger>Hover</TooltipTrigger>
  <TooltipContent className="TooltipContent">Animated tooltip</TooltipContent>
</Tooltip>
.TooltipContent {
  transform-origin: var(--gentleduck-tooltip-content-transform-origin);
  transition: transform 150ms ease, opacity 150ms ease;
}
 
.TooltipContent[data-state='closed'] {
  opacity: 0;
  transform: scale(0.95);
}
 
.TooltipContent[data-state='delayed-open'],
.TooltipContent[data-state='instant-open'] {
  opacity: 1;
  transform: scale(1);
}
.TooltipContent {
  transform-origin: var(--gentleduck-tooltip-content-transform-origin);
  transition: transform 150ms ease, opacity 150ms ease;
}
 
.TooltipContent[data-state='closed'] {
  opacity: 0;
  transform: scale(0.95);
}
 
.TooltipContent[data-state='delayed-open'],
.TooltipContent[data-state='instant-open'] {
  opacity: 1;
  transform: scale(1);
}

Styling Hooks

  • data-state - Set on trigger and content (closed, delayed-open, instant-open) for state-based styling.
  • data-side - Set on content (top, right, bottom, left) for placement-aware styles.
  • --gentleduck-tooltip-content-transform-origin - CSS variable for animation transform origin.

API Reference

TooltipProvider

PropTypeDefaultDescription
childrenReact.ReactNode--Tooltip tree to provide behavior for
delayDurationnumber700Delay before opening tooltips
skipDelayDurationnumber300Window where moving between triggers skips delay
disableHoverableContentbooleanfalseClose tooltip as soon as pointer leaves trigger

Tooltip

PropTypeDefaultDescription
childrenReact.ReactNode--Tooltip sub-components (TooltipTrigger, TooltipContent)
openboolean--Controlled open state
defaultOpenbooleanfalseUncontrolled initial open state
onOpenChange(open: boolean) => void--Callback when open state changes
delayDurationnumber700Per-tooltip delay override
disableHoverableContentbooleanfalsePer-tooltip hover-content behavior override
dir'ltr' | 'rtl'--Text direction. Resolved by primitives useDirection (dir prop -> DirectionProvider -> 'ltr').
...propsReact.ComponentPropsWithRef<typeof TooltipPrimitive.Root>--Additional root props

TooltipTrigger

PropTypeDefaultDescription
asChildbooleanfalseRenders the child element as the trigger instead of a button
childrenReact.ReactNode--Content rendered inside the trigger
classNamestring--Additional CSS class names to apply
...propsOmit<React.ComponentPropsWithRef<typeof TooltipPrimitive.Trigger>, 'size'>--Additional props to spread to the button element

TooltipContent

PropTypeDefaultDescription
classNamestring--Additional CSS class names to apply
childrenReact.ReactNode--Content rendered inside the tooltip
refReact.Ref<HTMLDivElement>--Ref forwarded to the content container
forceMountboolean--Keep content mounted for external animation control
side'top' | 'right' | 'bottom' | 'left''top'Preferred side relative to the trigger
align'start' | 'center' | 'end''center'Alignment on the chosen side
sideOffsetnumber4Main-axis offset from trigger
alignOffsetnumber0Cross-axis offset from trigger
...propsReact.ComponentPropsWithRef<typeof TooltipPrimitive.Content>--Additional props to spread to the content div

RTL Support

Set dir="rtl" on Tooltip for a local override, or set DirectionProvider once at app/root level for global direction. This mirrors tooltip positioning in right-to-left layouts.

<TooltipProvider>
  <Tooltip dir="rtl">
    <TooltipTrigger>مرر الماوس</TooltipTrigger>
    <TooltipContent>نص التلميح</TooltipContent>
  </Tooltip>
</TooltipProvider>
<TooltipProvider>
  <Tooltip dir="rtl">
    <TooltipTrigger>مرر الماوس</TooltipTrigger>
    <TooltipContent>نص التلميح</TooltipContent>
  </Tooltip>
</TooltipProvider>