Skip to main content

Installation

npx shadcn@latest add tooltip

Setup

Add the TooltipProvider to the root of your app:
app/layout.tsx
import { TooltipProvider } from "@/components/ui/tooltip"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <TooltipProvider>{children}</TooltipProvider>
      </body>
    </html>
  )
}

Usage

import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"
<Tooltip>
  <TooltipTrigger>Hover</TooltipTrigger>
  <TooltipContent>
    <p>Add to library</p>
  </TooltipContent>
</Tooltip>

Examples

Basic

A simple tooltip with text content.
import { Button } from "@/components/ui/button"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"

export default function TooltipDemo() {
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="outline">Hover</Button>
      </TooltipTrigger>
      <TooltipContent>
        <p>Add to library</p>
      </TooltipContent>
    </Tooltip>
  )
}

Positioning

Control the position of the tooltip using the side prop.

    With Arrow

    Tooltips include an arrow by default pointing to the trigger element.
    import { Button } from "@/components/ui/button"
    import {
      Tooltip,
      TooltipContent,
      TooltipTrigger,
    } from "@/components/ui/tooltip"
    
    export default function TooltipWithArrow() {
      return (
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Hover me</Button>
          </TooltipTrigger>
          <TooltipContent>
            <p>This tooltip has an arrow</p>
          </TooltipContent>
        </Tooltip>
      )
    }
    

    Keyboard Shortcut

    Display keyboard shortcuts in tooltips.
    import { Button } from "@/components/ui/button"
    import {
      Tooltip,
      TooltipContent,
      TooltipTrigger,
    } from "@/components/ui/tooltip"
    
    export default function TooltipKeyboard() {
      return (
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Save</Button>
          </TooltipTrigger>
          <TooltipContent>
            <p className="flex items-center gap-2">
              Save changes
              <kbd className="pointer-events-none inline-flex h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground opacity-100">
                <span className="text-xs"></span>S
              </kbd>
            </p>
          </TooltipContent>
        </Tooltip>
      )
    }
    

    Disabled Button

    Show a tooltip on a disabled button by wrapping it with a span.
    import { Button } from "@/components/ui/button"
    import {
      Tooltip,
      TooltipContent,
      TooltipTrigger,
    } from "@/components/ui/tooltip"
    
    export default function TooltipDisabled() {
      return (
        <Tooltip>
          <TooltipTrigger asChild>
            <span className="inline-block">
              <Button disabled>Disabled</Button>
            </span>
          </TooltipTrigger>
          <TooltipContent>
            <p>This action is currently disabled</p>
          </TooltipContent>
        </Tooltip>
      )
    }
    

    Delay Duration

    Control how long the user must hover before the tooltip appears.
    import { Button } from "@/components/ui/button"
    import {
      Tooltip,
      TooltipContent,
      TooltipTrigger,
    } from "@/components/ui/tooltip"
    
    export default function TooltipDelay() {
      return (
        <Tooltip delayDuration={0}>
          <TooltipTrigger asChild>
            <Button variant="outline">Instant</Button>
          </TooltipTrigger>
          <TooltipContent>
            <p>This tooltip appears instantly</p>
          </TooltipContent>
        </Tooltip>
      )
    }
    

    Rich Content

    Tooltips can contain rich content, not just text.
    import { Button } from "@/components/ui/button"
    import {
      Tooltip,
      TooltipContent,
      TooltipTrigger,
    } from "@/components/ui/tooltip"
    
    export default function TooltipRichContent() {
      return (
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Hover for details</Button>
          </TooltipTrigger>
          <TooltipContent className="max-w-xs">
            <div className="space-y-2">
              <p className="font-semibold">Feature Details</p>
              <p className="text-xs text-muted-foreground">
                This feature allows you to customize the appearance and behavior of
                your components.
              </p>
            </div>
          </TooltipContent>
        </Tooltip>
      )
    }
    

    Component Code

    "use client"
    
    import * as React from "react"
    import { Tooltip as TooltipPrimitive } from "radix-ui"
    
    import { cn } from "@/lib/utils"
    
    function TooltipProvider({
      delayDuration = 0,
      ...props
    }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
      return (
        <TooltipPrimitive.Provider
          data-slot="tooltip-provider"
          delayDuration={delayDuration}
          {...props}
        />
      )
    }
    
    function Tooltip({
      ...props
    }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
      return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
    }
    
    function TooltipTrigger({
      ...props
    }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
      return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
    }
    
    function TooltipContent({
      className,
      sideOffset = 0,
      children,
      ...props
    }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
      return (
        <TooltipPrimitive.Portal>
          <TooltipPrimitive.Content
            data-slot="tooltip-content"
            sideOffset={sideOffset}
            className={cn(
              "z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
              className
            )}
            {...props}
          >
            {children}
            <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
          </TooltipPrimitive.Content>
        </TooltipPrimitive.Portal>
      )
    }
    
    export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
    

    Props

    TooltipProvider

    PropTypeDefaultDescription
    delayDurationnumber0Time in ms before tooltip appears
    skipDelayDurationnumber300Time before delay resets after pointer leaves
    disableHoverableContentbooleanfalsePrevents hovering over tooltip content

    Tooltip

    PropTypeDefaultDescription
    openboolean-Controlled open state
    defaultOpenbooleanfalseDefault open state (uncontrolled)
    onOpenChange(open: boolean) => void-Callback when open state changes
    delayDurationnumber-Overrides provider’s delayDuration

    TooltipTrigger

    PropTypeDefaultDescription
    asChildbooleanfalseMerge props with child element
    classNamestring-Additional CSS classes

    TooltipContent

    PropTypeDefaultDescription
    side"top" | "right" | "bottom" | "left""top"Preferred side of the trigger
    sideOffsetnumber0Distance from trigger in pixels
    align"start" | "center" | "end""center"Alignment relative to trigger
    alignOffsetnumber0Offset from alignment position
    classNamestring-Additional CSS classes

    Accessibility

    • Automatically associates tooltip with trigger using aria-describedby
    • Keyboard accessible (opens on focus, closes on blur/escape)
    • Uses role="tooltip" for screen readers
    • Portal rendering prevents z-index issues
    • Respects prefers-reduced-motion

    Best Practices

    • Keep tooltip text concise and informative
    • Don’t use tooltips for critical information
    • Ensure tooltips don’t hide important content
    • Use asChild on trigger to merge with custom components
    • Wrap disabled buttons in a span to enable tooltips
    • Consider using instant tooltips (delayDuration={0}) for frequently accessed items

    API Reference

    Built on top of Radix UI Tooltip. See the official API documentation for more details.

    Build docs developers (and LLMs) love