10 shadcn Tooltip Component Guide: Examples, Variants & Code

Complete guide to the shadcn/ui Tooltip component with 10 working code examples: positioning, keyboard shortcuts, disabled buttons, delay, rich content, avatars, form validation, custom styling, and controlled mode.

AshFull-stack developer and UI/UX enthusiast.
Published Jun 9, 2026
Updated Jun 9, 2026
11 min read
shadcn/uiReactNext.jsTooltipUI ComponentsTypeScriptTailwind CSSAccessibilityFrontend Development
shadcn/ui Tooltip component guide cover image showing animated tooltip examples on a Next.js dashboard

The shadcn/ui Tooltip component displays contextual information when users hover over or focus on a UI element. It is built on Radix UI primitives, ships with full keyboard and screen reader accessibility out of the box, and is installed in one command: npx shadcn@latest add tooltip. Every tooltip requires a <TooltipProvider> in your root layout and composes three sub-components: Tooltip, TooltipTrigger, and TooltipContent.

In this guide, I'll walk through 10 practical shadcn tooltip variants — from the basic implementation to keyboard shortcut hints, disabled button tooltips, rich content, form validation, and custom styling. Each example includes a working code snippet you can drop straight into a Next.js project.

Prerequisites

Before you build with the shadcn tooltip, you need a working shadcn/ui setup with the tooltip component added. Here is what your project needs:

Install the tooltip component with the CLI:

Terminal

Then add <TooltipProvider> to your root layout. This is a required step — tooltips will not render without it:

app/layout.tsx

One important note: avoid wrapping each individual tooltip with its own <TooltipProvider>. That pattern creates redundant providers on every render and causes a noticeable performance hit in apps with many tooltips. One provider at the root handles everything.

Basic Implementation

The base shadcn tooltip wraps a trigger element and shows content on hover or keyboard focus. This is the three-part composition you will use for every variant in this guide.

What you get:

A fully accessible tooltip that appears on hover and keyboard focus, disappears on mouse-out or blur, and is announced by screen readers via the correct ARIA role.

  • asChild on TooltipTrigger renders the button as the trigger instead of wrapping it in an extra element
  • TooltipContent accepts any valid JSX, not just text
  • The default delayDuration is 0 ms — the tooltip appears immediately

10 shadcn Tooltip Variants (with Code Examples)

These patterns cover the full range of real use cases: positioning, keyboard hints, disabled elements, delays, rich content, icons, avatars, form validation, and custom styling.

1. Tooltip Positioning (top, right, bottom, left)

Control which side the tooltip appears on using the side prop on TooltipContent. The four values are top, right, bottom, and left. Radix UI automatically flips the tooltip if it would overflow the viewport.

When to use:

Use side="top" for toolbar icons, side="right" for sidebar items, and side="bottom" for table actions where upward overflow is a concern.

2. Tooltip with Keyboard Shortcut

A keyboard shortcut tooltip shows the hotkey that triggers an action alongside the action label. This pattern is common in editors, design tools, and dashboards with power users. Use <kbd> elements inside TooltipContent to render styled key indicators.

When to use:

Any toolbar button, command palette trigger, or action with a known keyboard shortcut. The <kbd> elements are semantic HTML — screen readers announce them as keyboard input.

3. Tooltip on a Disabled Button

Disabled elements do not fire pointer events, so a tooltip on a disabled button will not appear by default. The fix is to wrap the disabled button in a <span> and use that span as the trigger. This gives the tooltip a live pointer target while keeping the button disabled.

When to use:

Form submit buttons, actions that require a completed step, or anything gated behind a condition. Always explain why the button is disabled — that is the entire point of this pattern.

4. Tooltip with Custom Delay

The delayDuration prop on <TooltipProvider> sets the millisecond delay before any tooltip in the tree appears. The default is 0. For information-dense dashboards, a 300–500 ms delay prevents tooltips from appearing on every accidental hover.

When to use:

Use delayDuration={0} for icon-only buttons where the label is critical. Use delayDuration={300} or higher for supplementary hints on elements that already have visible labels.

5. Tooltip with Rich Content

TooltipContent accepts any JSX — not just a string. A rich content tooltip can include a title, description, badge, or icon combination. Keep content short enough to scan in under 2 seconds; if it needs more space, a Popover is the right component instead.

When to use:

Help icons next to pricing tiers, feature flags, or settings fields where a short label isn't enough. Add max-w-[200px] on TooltipContent to keep it readable.

6. Tooltip on an Icon Button

Icon-only buttons have no visible label, making them inaccessible without additional context. A shadcn tooltip on an icon button serves as the visible label. Always pair it with a <span className="sr-only"> inside the button for screen readers, and keep the tooltip text identical to the action.

When to use:

Toolbar action rows in dashboards, table row actions, header icon navigation. This is the most common real-world tooltip pattern in SaaS products. If you're building a full dashboard layout, see our resizable sidebar component guide for the surrounding structure these icon toolbars live in.

7. Tooltip on an Avatar

An avatar tooltip shows user metadata — full name, role, or status — when hovering over a compact avatar display. This is especially useful in team dashboards, comment threads, or any UI where avatars stand in for full user cards.

ACBMSK

When to use:

Stacked avatar rows showing team members, collaborators on a file, or people assigned to a task. Pairs well with the shadcn avatar component guide for fuller avatar implementation patterns.

8. Tooltip for Form Validation Errors

A validation tooltip surfaces an inline error message next to the field that caused it, without breaking the layout with an error block below every input. Use open and onOpenChange in controlled mode to show the tooltip only when a field has an error.

When to use:

Compact forms where error messages below each field would collapse the layout. Use open={!!error} for controlled display — the tooltip only appears when the error state is truthy.

Pro tip: This pattern pairs well with shadcn select fields too. Dropdown validation errors benefit from the same tooltip approach. See the shadcn select component guide for form composition patterns.

9. Custom Styled Tooltip

The default shadcn tooltip uses bg-primary text-primary-foreground for its content background. You can override this per-tooltip using Tailwind utility classes directly on TooltipContent. Custom styled tooltips are useful for status indicators, warnings, and contextual colour coding.

When to use:

Status badges, health indicators, and alert contexts where colour communicates urgency. Match the tooltip colour to the element it describes — don't use red tooltips on green status indicators.

10. Controlled Tooltip (programmatic open/close)

By default the shadcn tooltip is uncontrolled — it opens on hover and closes on mouse-out. A controlled tooltip uses open and onOpenChange props on the <Tooltip> root to manage visibility from your component's state. This is useful for tutorial flows, onboarding hints, and async-triggered feedback.

ControlledTooltip.tsx

When to use:

Copy-to-clipboard feedback, onboarding steps that highlight a feature, or contextual hints in a chat interface where message actions appear on hover. The setTimeout pattern auto-dismisses after a set duration.

Animated Tooltip Variants

The default shadcn tooltip already animates — it uses animate-in fade-in-0 zoom-in-95 on open and animate-out fade-out-0 zoom-out-95 on close. You can replace that animation entirely by passing a Tailwind arbitrary animate-[...] class to TooltipContent. Since className is merged after the defaults via cn(), the later class wins and replaces the built-in animation.

Define your @keyframes in a <style> tag inside the component (they are global once rendered) and reference them by name in the arbitrary value. Four variants that cover the most useful motion patterns:

How each variant works:

  • Bounce — scales past 1.0 at 55% then settles back, giving a physical spring feel
  • Slide Up — pure translateY with no scale, clean and minimal
  • Elastic — same overshoot pattern but driven by a spring cubic-bezier(0.34, 1.56, 0.64, 1) passed via style (avoids Tailwind's arbitrary value parser struggling with parentheses in the shorthand)
  • Drift — 4 px vertical translate with a gentle ease, the subtlest option for content-heavy UIs where motion should not compete for attention

Note: These custom animations replace the default open animation only. The close animation (data-[state=closed]:animate-out) remains intact. If you want to animate close too, add a corresponding data-[state=closed]:animate-[...] class alongside the open one.

Cursor-Following Tooltip

The shadcn TooltipContent is positioned relative to the trigger via Radix UI's Popper primitive, so it cannot track the mouse pointer directly. To get a tooltip that follows the cursor as it moves, build a small custom component with framer-motion: track onMouseMove coordinates with useMotionValue, smooth them with useSpring, and fade the tooltip in and out with AnimatePresence.

Move your mouse over this area

How it works:

  • onMouseMove reads the cursor position relative to the container with getBoundingClientRect() and writes it to two useMotionValue instances
  • useSpring wraps each motion value so the tooltip glides toward the cursor instead of snapping to it on every pixel of movement
  • x: "-50%", y: "calc(-100% - 12px)" centers the tooltip horizontally above the cursor with a small gap
  • AnimatePresence handles the enter/exit fade so the tooltip appears on hover and disappears cleanly when the pointer leaves

When to use:

Image galleries, canvas or chart annotations, and creative portfolio sites where a label should track a large hover target rather than stay pinned to one corner. For standard buttons, icons, and form fields, the built-in TooltipContent positioning from the variants above is simpler and more accessible — reach for a cursor-following tooltip only when the visual effect is the point.

Animated Avatar Group Tooltip

A popular pattern for "who's on this team" or "who worked on this" displays — a stacked group of avatars where hovering one springs up a name-and-role card that tilts based on where the cursor enters the avatar. It is built the same way as the cursor-following tooltip: framer-motion drives the position and rotation, but here the values come from the cursor's x offset within each avatar rather than from the page coordinates.

JD
RJ
JS
ED
TD

How it works:

  • onMouseMove tracks the cursor's horizontal offset from the center of the avatar using event.nativeEvent.offsetX minus half the element's width
  • useTransform maps that offset to a rotation range (-45deg to 45deg) and a horizontal shift (-50px to 50px), so the card tilts toward the side the cursor entered from
  • useSpring smooths both values for a natural, slightly bouncy tilt instead of an instant snap
  • AnimatePresence with mode="popLayout" handles the spring-in/scale-out transition as the user moves between avatars
  • group-hover:z-30 group-hover:scale-105 lifts the hovered avatar above its neighbours in the stack

When to use:

Team pages, "contributors" lists, testimonial avatar groups, and project collaborator stacks. Pairs with the shadcn avatar component guide for the underlying Avatar setup. As with the cursor-following tooltip, this is a decorative custom component rather than the accessible Tooltip primitive — keep the avatar's alt text or an aria-label in place so screen reader users still get the name and role.

Common Issues with shadcn Tooltip (and Fixes)

The shadcn tooltip is straightforward to implement, but five issues appear consistently in real apps. Here is what causes each one and how to fix it.

1. Tooltip does not show on hover

The most common cause is a missing <TooltipProvider> in the root layout. Tooltips will not open without a provider in the tree.

Fix: Add <TooltipProvider> to app/layout.tsx wrapping { children }. One provider at the root is enough for the entire app.

2. Tooltip does not show on a disabled button

Disabled HTML elements suppress pointer events, so the hover that triggers the tooltip never fires.

Fix: Wrap the disabled button in a <span tabIndex={0}> and use the span as the TooltipTrigger. Add pointer-events-none to the button itself to keep it unclickable.

3. Performance slowdown with many tooltips

Wrapping each individual tooltip in its own <TooltipProvider> creates a new provider instance on every render. In one documented case, 60 tooltip instances caused a measurable performance regression.

Fix: Use a single <TooltipProvider> at the app root. Never put a provider inside a loop or a component that renders repeatedly.

4. Tooltip clips at the viewport edge

Radix UI automatically repositions the tooltip to avoid overflow, but this requires the trigger element to be inside a correctly positioned container.

Fix: Use sideOffset on TooltipContent to add spacing between the tooltip and the trigger, and rely on the side prop to set your preferred direction. Radix handles the fallback automatically.

5. Tooltip does not close after clicking a trigger

Uncontrolled tooltips stay open when the trigger is clicked if click does not move focus away from the element.

Fix: Switch to controlled mode with open and onOpenChange, and set open={false} on click. Or use disableHoverableContent on the provider to prevent the tooltip from staying open when the user moves to the tooltip content itself.

When to use a shadcn Tooltip vs a Popover

The tooltip and popover look similar but serve different purposes. A tooltip is non-interactive — it displays supplementary text and disappears. A popover is interactive — it can contain buttons, links, forms, and persistent content.

Use a Tooltip when:

  • The content is 1–2 lines of non-interactive text
  • The hint supplements a visible element (icon button label, feature name)
  • The content never needs to be clicked or focused

Use a Popover when:

  • The content includes links, buttons, inputs, or any interactive elements
  • The content stays open while the user interacts with it
  • The content is longer than 2 short lines

Rule of thumb: If a user needs to click something inside the overlay, use a Popover. If they only need to read something, use a Tooltip.

Conclusion

The shadcn tooltip component handles the most common contextual hint patterns in one composable API: Tooltip, TooltipTrigger, and TooltipContent. The 10 variants in this guide cover every production scenario you'll encounter: positioning, keyboard shortcuts, disabled buttons, delay control, rich content, icon buttons, avatars, form validation, custom styling, and programmatic control.

The two rules that will save you the most time: add <TooltipProvider> once at the root layout and never again inside individual components, and wrap disabled buttons in a span when you need a tooltip on them.

If you're building a SaaS dashboard, landing page, or admin panel with shadcn/ui, our step-by-step Next.js + shadcn/ui guide walks through the full project structure. Or skip straight to a finished starting point with a shadcn/ui landing page template — every template is built on Next.js App Router, Tailwind CSS, and TypeScript with the exact patterns shown in this guide.

Download Free Shadcn Landing Page Templates

Explore more shadcn component guides:

FAQ

A
Ash

Full-stack developer and UI/UX enthusiast.

Related Articles