Tooltip

Tooltip

A popup that labels its trigger on hover or focus, built on Base UI. Positioned on any side, with an arrow by default.

Playground

content
variant
side
<Tooltip>
  <TooltipTrigger render={<Button variant="outline" />}>
    Hover me
  </TooltipTrigger>
  <TooltipContent>Open source by default</TooltipContent>
</Tooltip>

Preview

Wrap the app (or a subtree) in TooltipProvider, then compose Tooltip, TooltipTrigger and TooltipContent. The trigger's render prop merges it onto your own element.

<TooltipProvider>
  <Tooltip>
    <TooltipTrigger render={<Button variant="outline" />}>
      Hover me
    </TooltipTrigger>
    <TooltipContent>
      Open source by default <Diamond className="size-3 shrink-0" />
    </TooltipContent>
  </Tooltip>
</TooltipProvider>

Installation

npx shadcn@latest add https://espresso-base-ui.vercel.app/r/tooltip.json

Usage

import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip"
<TooltipProvider>
  <Tooltip>
    <TooltipTrigger render={<Button variant="outline" />}>
      Trigger
    </TooltipTrigger>
    <TooltipContent side="bottom">Label</TooltipContent>
  </Tooltip>
</TooltipProvider>

Variants

variant on TooltipContent: default is the primary-colored bubble with an arrow, plain drops the arrow, and subtle swaps to a background-colored surface.

<TooltipContent variant="default">Default variant</TooltipContent>
<TooltipContent variant="plain">Plain variant</TooltipContent>
<TooltipContent variant="subtle">Subtle variant</TooltipContent>

Placement

side places the tooltip on any edge of the trigger, align shifts it along that edge, and sideOffset / alignOffset fine-tune the distance – the enter animation follows the chosen side.

<TooltipContent side="top">Top tooltip</TooltipContent>
<TooltipContent side="bottom">Bottom tooltip</TooltipContent>
<TooltipContent side="right" align="start" sideOffset={8}>
  Right, aligned to start
</TooltipContent>

Custom triggers

Any element works as a trigger via render – here an inline text span; long content wraps within the bubble's max-w-xs.

Hover over the underlined text to see the tooltip.

<p>
  Hover over the{" "}
  <Tooltip>
    <TooltipTrigger
      render={<span className="cursor-help underline decoration-dotted" />}
    >
      underlined text
    </TooltipTrigger>
    <TooltipContent>This is an inline tooltip</TooltipContent>
  </Tooltip>{" "}
  to see the tooltip.
</p>

API reference

All four parts are thin wrappers around the Base UI tooltip and accept its full prop surface – TooltipProvider and Tooltip configure timing and open state, TooltipContent bundles the portal, positioner, popup and arrow into one styled component.

TooltipProvider

PropTypeDefaultDescription
delaynumber150Milliseconds to wait before opening a tooltip; tooltips under one provider share it and skip the delay when moving between triggers.

TooltipContent

PropTypeDefaultDescription
variant"default" | "plain" | "subtle""default"default: primary bubble with arrow. plain: same bubble, no arrow. subtle: background-colored surface with arrow.
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""top"Which side of the trigger to place against.
sideOffsetnumber4Gap in pixels between trigger and tooltip.
align"start" | "center" | "end""center"Alignment along the chosen side.
alignOffsetnumber0Extra offset in pixels along the align axis.
ComponentDescription
TooltipProviderShares open delay and timing across tooltips (data-slot="tooltip-provider"). Mount once around the subtree.
TooltipRoot that pairs one trigger with its content (data-slot="tooltip"). Accepts Base UI root props like open, onOpenChange and defaultOpen.
TooltipTriggerThe hoverable/focusable element (data-slot="tooltip-trigger"). Use render to merge onto your own element, e.g. a Button.
TooltipContentPortalled, positioned popup (data-slot="tooltip-content") that also reflects data-variant. Renders the arrow unless variant="plain".

Accessibility & styling hooks

The Base UI primitive wires the trigger and popup together with the proper ARIA attributes, opens on keyboard focus as well as hover, and closes on Escape – so tooltip content stays discoverable without a pointer. Keep the content to a short label; interactive controls don't belong in a tooltip. For styling, every part exposes its data-slot, and the popup reflects Base UI state as data-open / data-closed plus data-side for per-placement animation – embedded Kbd elements (matched via data-slot="kbd") get tightened trailing padding automatically.

Search documentation

Search the component docs