Skip to content

Tooltip

A <Tooltip> is a hover-revealed label. It exists so an icon-only or terse control still has a name. Anything longer than a sentence belongs in body copy, an Alert, or a Popover.

import from "@na/ui/components/tooltip" ▶ Open in Storybook packages/ui/src/components/tooltip.tsx

When to use

  • Icon-only buttons (always — paired with an aria-label).
  • Truncated labels (<TooltipContent> shows the full string).
  • Power-user shortcuts (Save · ⌘S).

Don’t use Tooltip for: error messages on form fields (FormError), persistent guidance (Alert), or restating a visible label.

Basic

<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline">Hover me</Button>
</TooltipTrigger>
<TooltipContent>This explains what the button does.</TooltipContent>
</Tooltip>
</TooltipProvider>

Icon-only button

For icon-only buttons, the Tooltip is the only visible label. Add aria-label on the button so screen readers announce it without hover.

<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon-sm" aria-label="Help">
<IconHelp />
</Button>
</TooltipTrigger>
<TooltipContent>What is this?</TooltipContent>
</Tooltip>
</TooltipProvider>

Sides

<TooltipContent side="top" /> {/* default */}
<TooltipContent side="right" />
<TooltipContent side="bottom" />
<TooltipContent side="left" />

API

TooltipProvider

Prop Type Default Description
delayDuration number 0 Milliseconds before the tooltip opens on hover.
skipDelayDuration number 300 After this long without a hover, the next hover re-applies delayDuration.

TooltipContent

Prop Type Default Description
side "top" | "right" | "bottom" | "left" "top" Preferred side. Radix flips automatically if there is no room.
sideOffset number 0 Distance in px between trigger and tooltip.
align "start" | "center" | "end" "center" Alignment along the chosen side.

Design guidelines

✓ Do

  • Pair every icon-only button with both a Tooltip and an aria-label so the action has a name in two channels.
  • Keep content under one sentence. If you need two, use a Popover.
  • Use the Button primitive's built-in tooltip prop when the trigger is a button — it composes Tooltip for you.

✗ Don't

  • Restate the visible label in the tooltip ("Save" → tooltip "Save"). That is noise.
  • Put interactive controls inside TooltipContent. Tooltips are not popovers.
  • Open a tooltip on a disabled button without setting disabledReason — see Button.

Accessibility

  • Provided by Radix: keyboard focus opens the tooltip; Esc closes it.
  • Screen readers receive the TooltipContent text via aria-describedby on the trigger.
  • For icon-only buttons, set aria-label on the trigger — screen readers don’t wait for hover.
  • A disabled <button> doesn’t fire hover events; <Button> handles this for you (wraps the disabled button so the wrapper picks up hover). For raw triggers, wrap manually.
  • Button — Has a tooltip prop that composes this primitive.
  • Popover — For interactive, click-driven content.
  • Alert — For persistent inline messages.

▶ Open Tooltip stories in Storybook