import { Tooltip, TooltipContent, TooltipTrigger } from "@registry/components/ui/tooltip";
import { Button } from "@registry/components/ui/button";

export function TooltipExample() {
  return (
    <Tooltip>
      <TooltipTrigger render={<Button variant="outline">Hover me</Button>} />
      <TooltipContent>
        <p>Add to library</p>
      </TooltipContent>
    </Tooltip>
  );
}

Examples

Positioning

Use positionerProps on TooltipContent to control which side the tooltip appears on.

import { Tooltip, TooltipContent, TooltipTrigger } from "@registry/components/ui/tooltip";
import { Button } from "@registry/components/ui/button";

export function TooltipPositionedExample() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline">Top</Button>} />
        <TooltipContent positionerProps={{ side: "top" }}>
          <p>Tooltip on top</p>
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline">Bottom</Button>} />
        <TooltipContent positionerProps={{ side: "bottom" }}>
          <p>Tooltip on bottom</p>
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline">Left</Button>} />
        <TooltipContent positionerProps={{ side: "left" }}>
          <p>Tooltip on left</p>
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger render={<Button variant="outline">Right</Button>} />
        <TooltipContent positionerProps={{ side: "right" }}>
          <p>Tooltip on right</p>
        </TooltipContent>
      </Tooltip>
    </div>
  );
}

Hover delay

Control the open delay with the delay prop on TooltipTrigger.

import { Tooltip, TooltipContent, TooltipTrigger } from "@registry/components/ui/tooltip";
import { Button } from "@registry/components/ui/button";

export function TooltipWithDelayExample() {
  return (
    <div className="flex flex-wrap items-center gap-4">
      <Tooltip>
        <TooltipTrigger delay={0} render={<Button variant="outline">Instant</Button>} />
        <TooltipContent>
          <p>Appears immediately</p>
        </TooltipContent>
      </Tooltip>
      <Tooltip>
        <TooltipTrigger delay={500} render={<Button variant="outline">Delayed</Button>} />
        <TooltipContent>
          <p>Appears after 500ms</p>
        </TooltipContent>
      </Tooltip>
    </div>
  );
}

Animated shared tooltip

Use createTooltipHandle with TooltipProvider to share a single tooltip across multiple triggers. The TooltipViewport component animates the content when moving between triggers.

import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider, TooltipViewport, createTooltipHandle } from "@registry/components/ui/tooltip";
import { Button } from "@registry/components/ui/button";
import { PencilSimpleIcon, TrashIcon, CopyIcon, DownloadSimpleIcon, ShareNetworkIcon } from "@phosphor-icons/react";

export function TooltipAnimatedExample() {
  const tooltipHandle = createTooltipHandle<string>();

  return (
    <TooltipProvider>
      <div className="flex items-center gap-2">
        <TooltipTrigger
          handle={tooltipHandle}
          payload="Edit this item"
          render={
            <Button size="icon" variant="ghost">
              <PencilSimpleIcon />
            </Button>
          }
        />
        <TooltipTrigger
          handle={tooltipHandle}
          payload="Duplicate"
          render={
            <Button size="icon" variant="ghost">
              <CopyIcon />
            </Button>
          }
        />
        <TooltipTrigger
          handle={tooltipHandle}
          payload="Download"
          render={
            <Button size="icon" variant="ghost">
              <DownloadSimpleIcon />
            </Button>
          }
        />
        <TooltipTrigger
          handle={tooltipHandle}
          payload="Share"
          render={
            <Button size="icon" variant="ghost">
              <ShareNetworkIcon />
            </Button>
          }
        />
        <TooltipTrigger
          handle={tooltipHandle}
          payload="Delete"
          render={
            <Button size="icon" variant="ghost">
              <TrashIcon />
            </Button>
          }
        />
      </div>
      <Tooltip handle={tooltipHandle}>
        {({ payload }) => (
          <TooltipContent>
            {typeof payload === "string" && (
              <TooltipViewport>
                <span>{payload}</span>
              </TooltipViewport>
            )}
          </TooltipContent>
        )}
      </Tooltip>
    </TooltipProvider>
  );
}

Installation

Copy the source code below into your project:

import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
import { cn } from "@registry/lib/utils";

const createTooltipHandle = BaseTooltip.createHandle;

function Tooltip({ children, ...props }: BaseTooltip.Root.Props) {
  return (
    <BaseTooltip.Root data-slot="tooltip" {...props}>
      {children}
    </BaseTooltip.Root>
  );
}

function TooltipProvider({ ...props }: BaseTooltip.Provider.Props) {
  return <BaseTooltip.Provider data-slot="tooltip-provider" {...props} />;
}

function TooltipTrigger({ delay = 100, ...props }: BaseTooltip.Trigger.Props) {
  return <BaseTooltip.Trigger delay={delay} data-slot="tooltip-trigger" {...props} />;
}

function TooltipContent({
  className,
  children,
  positionerProps,
  portalProps,
  ...props
}: BaseTooltip.Popup.Props & {
  positionerProps?: BaseTooltip.Positioner.Props;
  portalProps?: BaseTooltip.Portal.Props;
}) {
  return (
    <BaseTooltip.Portal {...portalProps}>
      <BaseTooltip.Positioner
        alignOffset={positionerProps?.alignOffset || 0}
        className={cn(
          "outline-none",
          "h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom,transform] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none",
          positionerProps?.className,
        )}
        side={positionerProps?.side || "top"}
        sideOffset={positionerProps?.sideOffset || 4}
        {...positionerProps}
      >
        <BaseTooltip.Popup
          className={cn(
            "flex h-(--popup-height,auto) w-(--popup-width,auto) origin-(--transform-origin) flex-col rounded-lg bg-gray-900 px-3 py-2 text-sm text-gray-50 transition-[transform,scale,opacity] data-ending-style:scale-90 data-ending-style:opacity-0 data-instant:duration-0 data-starting-style:scale-90 data-starting-style:opacity-0 dark:outline dark:-outline-offset-1 dark:outline-gray-700",
            className,
          )}
          data-slot="tooltip-popup"
          {...props}
        >
          {children}
        </BaseTooltip.Popup>
      </BaseTooltip.Positioner>
    </BaseTooltip.Portal>
  );
}

function TooltipViewport({
  children,
  className: _className,
  ...props
}: BaseTooltip.Viewport.Props) {
  return (
    <BaseTooltip.Viewport
      className="relative h-full w-full overflow-clip px-(--viewport-inline-padding) py-1 [--viewport-inline-padding:0.5rem] **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding))] **:data-current:translate-x-0 **:data-current:opacity-100 **:data-current:transition-[translate,opacity] **:data-current:duration-[350ms,175ms] **:data-current:ease-[cubic-bezier(0.22,1,0.36,1)] **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding))] **:data-previous:translate-x-0 **:data-previous:opacity-100 **:data-previous:transition-[translate,opacity] **:data-previous:duration-[350ms,175ms] **:data-previous:ease-[cubic-bezier(0.22,1,0.36,1)] data-[activation-direction~='left']:[&_[data-current][data-starting-style]]:-translate-x-1/2 data-[activation-direction~='left']:[&_[data-current][data-starting-style]]:opacity-0 data-[activation-direction~='right']:[&_[data-current][data-starting-style]]:translate-x-1/2 data-[activation-direction~='right']:[&_[data-current][data-starting-style]]:opacity-0 data-[activation-direction~='left']:[&_[data-previous][data-ending-style]]:translate-x-1/2 data-[activation-direction~='left']:[&_[data-previous][data-ending-style]]:opacity-0 data-[activation-direction~='right']:[&_[data-previous][data-ending-style]]:-translate-x-1/2 data-[activation-direction~='right']:[&_[data-previous][data-ending-style]]:opacity-0 [[data-instant]_&_[data-current]]:transition-none [[data-instant]_&_[data-previous]]:transition-none"
      data-slot="tooltip-viewport"
      {...props}
    >
      {children}
    </BaseTooltip.Viewport>
  );
}

export {
  Tooltip,
  TooltipProvider,
  TooltipTrigger,
  TooltipContent,
  TooltipViewport,
  createTooltipHandle,
};

API Reference

Tooltip

This component does not add any props on top of Base UI Tooltip.Root . See the Base UI docs for the full API reference.

TooltipTrigger

The TooltipTrigger component extends the Base UI Tooltip.Trigger props and adds the following:

PropTypeDefaultDescription
delaynumber100Delay in milliseconds before the tooltip opens.

TooltipContent

The TooltipContent component extends the Base UI Tooltip.Popup props and adds the following:

PropTypeDefaultDescription
positionerPropsBaseTooltip.Positioner.PropsProps forwarded to the underlying Positioner component. Includes align, side, sideOffset, alignOffset, collisionPadding, etc.
portalPropsBaseTooltip.Portal.PropsProps forwarded to the underlying Portal component.

TooltipViewport

This component does not add any props on top of Base UI Tooltip.Viewport . See the Base UI docs for the full API reference.

TooltipProvider

This component does not add any props on top of Base UI Tooltip.Provider . See the Base UI docs for the full API reference.