Skip to main content
Gremorie

Tooltip

Short non-essential context shown on hover and keyboard focus. Built on Radix Tooltip with a built-in arrow.

Overview

Tooltip is for non-critical supporting information: keyboard shortcuts, icon-button labels, "what does this do" hints. Always wrap your application root in a single TooltipProvider so every tooltip shares a delay timer and they don't pop in and out of sync.

Tooltips are not for critical information. Touch users may not be able to trigger them; keyboard users only see one while the trigger holds focus. If the user must read it, render it visibly in the layout. For rich previews, use HoverCard. For interactive content, use Popover.

The TooltipContent renders its own arrow automatically.

Preview

'use client';import { Button } from '@gremorie/rx-forms';import {  Tooltip,  TooltipContent,  TooltipProvider,  TooltipTrigger,} from '@gremorie/rx-overlays';export function TooltipPreview() {  return (    <TooltipProvider>      <Tooltip>        <TooltipTrigger asChild>          <Button variant="outline">Hover me</Button>        </TooltipTrigger>        <TooltipContent>          <p>Adds an item to the registry</p>        </TooltipContent>      </Tooltip>    </TooltipProvider>  );}

Anatomy

TooltipProvider              shares one delay timer across all tooltips
└─ Tooltip                   Radix root for a single tooltip instance
   ├─ TooltipTrigger         the hover/focus anchor element
   └─ TooltipContent         portalled bubble with a built-in arrow

Installation

bash npx gremorie@latest add rx-tooltip
bash pnpm dlx gremorie@latest add rx-tooltip
bash yarn dlx gremorie@latest add rx-tooltip
bash bunx --bun gremorie@latest add rx-tooltip

Usage

import { Button } from "@gremorie/rx-forms";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@gremorie/rx-overlays";

export function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="outline">Hover me</Button>
        </TooltipTrigger>
        <TooltipContent>
          <p>Adds an item to the registry</p>
        </TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

Mount a single TooltipProvider at your app root so every tooltip shares the same timing:

// app/layout.tsx
import { TooltipProvider } from '@gremorie/rx-overlays';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <TooltipProvider delayDuration={200}>{children}</TooltipProvider>
      </body>
    </html>
  );
}

The Angular edition of this component ships from source today (see the workbench for the side-by-side); its registry entry is coming next.

API

<TooltipProvider>

PropTypeDefaultDescription
delayDurationnumber0Default delay in milliseconds before tooltips open.
skipDelayDurationnumber300Time the user has after closing one tooltip to open another without delay.
disableHoverableContentbooleanfalseWhen true, tooltips close as soon as the pointer leaves the trigger.

Mount once at the application root. All tooltips share its timers.

<Tooltip>

Extends Radix Tooltip.Root. Notable props:

PropTypeDefaultDescription
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void-Fired when open state changes.
delayDurationnumberinherits from providerPer-tooltip override of the delay.

<TooltipTrigger>

Extends Radix Tooltip.Trigger. Pair with asChild to forward styles to a Button or any focusable element.

<TooltipContent>

PropTypeDefaultDescription
sideOffsetnumber0Pixel gap between trigger and content.
side"top" | "right" | "bottom" | "left""top"Preferred side; flips automatically when there is no room.
align"start" | "center" | "end""center"Alignment relative to the trigger axis.

Wrapped in a Radix Portal. The arrow indicator is included automatically.

Composition

  1. <TooltipProvider> at the app root sets the global delay.
  2. <Tooltip> owns the open state for one tooltip instance.
  3. <TooltipTrigger asChild> wraps a focusable element (button, link, icon button).
  4. <TooltipContent> mounts via Portal with the arrow and the text.

Variations

Sides

TooltipContent accepts a side prop (top, right, bottom, left) and flips automatically when there is no room. Hover or focus each trigger to see the tooltip and its arrow.

'use client';import { Button } from '@gremorie/rx-forms';import {  Tooltip,  TooltipContent,  TooltipProvider,  TooltipTrigger,} from '@gremorie/rx-overlays';const sides = ['top', 'right', 'bottom', 'left'] as const;export function TooltipSidesPreview() {  return (    <TooltipProvider delayDuration={200}>      <div className="flex flex-wrap gap-2">        {sides.map((side) => (          <Tooltip key={side}>            <TooltipTrigger asChild>              <Button variant="outline" className="capitalize">                {side}              </Button>            </TooltipTrigger>            <TooltipContent side={side}>Tooltip on the {side}</TooltipContent>          </Tooltip>        ))}      </div>    </TooltipProvider>  );}

Icon button label

The most common use. Pairs with the accessibility rule that every icon-only button needs a visible label affordance.

<Tooltip>
  <TooltipTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Copy link">
      <CopyIcon />
    </Button>
  </TooltipTrigger>
  <TooltipContent>Copy link</TooltipContent>
</Tooltip>

Keyboard shortcut hint

Pair with Kbd to surface shortcuts on dense toolbars.

<Tooltip>
  <TooltipTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Save">
      <SaveIcon />
    </Button>
  </TooltipTrigger>
  <TooltipContent>
    Save <Kbd className="ml-1">⌘S</Kbd>
  </TooltipContent>
</Tooltip>

Custom side and offset

Place tooltips above a row to avoid covering the next row.

<Tooltip>
  <TooltipTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Delete">
      <Trash2Icon />
    </Button>
  </TooltipTrigger>
  <TooltipContent side="top" sideOffset={6}>
    Delete row
  </TooltipContent>
</Tooltip>

Accessibility

  • Provider required: TooltipProvider is mandatory at the root; without it Radix throws.
  • Trigger: pointer hover and keyboard focus both open the tooltip.
  • Keyboard: focusing the trigger opens it; blurring closes; Esc closes when open.
  • Not a substitute for visible labels: icon-only buttons must also carry an aria-label. The tooltip is the visual hint; aria-label is the screen-reader name.
  • aria-describedby: linked automatically between trigger and content.
  • Touch: tooltips do not appear on touch by design. Keep their content non-critical.
  • Reduced motion: open/close animations honor prefers-reduced-motion.
  • Hover Card - hover-only rich preview for non-critical content.
  • Popover - click-driven, interactive overlay.
  • Button - the most common tooltip trigger.

On this page