Vela UI Docsvenizia.ai

Tooltip

A popup that displays contextual information when hovering or focusing on a trigger element.

npx shadcn@latest add @velaui/tooltip
npx shadcn@latest add @velaui/tooltip

Examples

  • Default
  • Accent
  • Sides
  • Align
  • Icon trigger

View code

Tooltip · Default

Installation

pnpm dlx velaui add @velaui/tooltip
pnpm dlx velaui add @velaui/tooltip

Code

tooltip-default.tsx
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="outline">Hover me</Button>
        </TooltipTrigger>
        <TooltipContent>Add to library</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

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

View code

Tooltip · Accent

Installation

pnpm dlx velaui add @velaui/tooltip
pnpm dlx velaui add @velaui/tooltip

Code

tooltip-accent.tsx
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="primary">Publish</Button>
        </TooltipTrigger>
        <TooltipContent variant="accent">Ready to ship</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger render={<Button variant="primary" />}>
          Publish
        </TooltipTrigger>
        <TooltipContent variant="accent">Ready to ship</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

View code

Tooltip · Sides

Installation

pnpm dlx velaui add @velaui/tooltip
pnpm dlx velaui add @velaui/tooltip

Code

tooltip-sides.tsx
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <div className="flex flex-wrap gap-2">
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Top</Button>
          </TooltipTrigger>
          <TooltipContent side="top">Above the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Right</Button>
          </TooltipTrigger>
          <TooltipContent side="right">Right of the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Bottom</Button>
          </TooltipTrigger>
          <TooltipContent side="bottom">Below the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline">Left</Button>
          </TooltipTrigger>
          <TooltipContent side="left">Left of the trigger</TooltipContent>
        </Tooltip>
      </div>
    </TooltipProvider>
  );
}
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <div className="flex flex-wrap gap-2">
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" />}>Top</TooltipTrigger>
          <TooltipContent side="top">Above the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" />}>Right</TooltipTrigger>
          <TooltipContent side="right">Right of the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" />}>Bottom</TooltipTrigger>
          <TooltipContent side="bottom">Below the trigger</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" />}>Left</TooltipTrigger>
          <TooltipContent side="left">Left of the trigger</TooltipContent>
        </Tooltip>
      </div>
    </TooltipProvider>
  );
}

View code

Tooltip · Align

Installation

pnpm dlx velaui add @velaui/tooltip
pnpm dlx velaui add @velaui/tooltip

Code

tooltip-align.tsx
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <div className="flex flex-wrap gap-2">
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline" size="sm">Start</Button>
          </TooltipTrigger>
          <TooltipContent side="bottom" align="start">Arrow at start</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline" size="sm">Middle</Button>
          </TooltipTrigger>
          <TooltipContent side="bottom" align="center">Arrow in the middle</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger asChild>
            <Button variant="outline" size="sm">End</Button>
          </TooltipTrigger>
          <TooltipContent side="bottom" align="end">Arrow at end</TooltipContent>
        </Tooltip>
      </div>
    </TooltipProvider>
  );
}
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <div className="flex flex-wrap gap-2">
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" size="sm" />}>
            Start
          </TooltipTrigger>
          <TooltipContent side="bottom" align="start">Arrow at start</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" size="sm" />}>
            Middle
          </TooltipTrigger>
          <TooltipContent side="bottom" align="center">Arrow in the middle</TooltipContent>
        </Tooltip>
        <Tooltip>
          <TooltipTrigger render={<Button variant="outline" size="sm" />}>
            End
          </TooltipTrigger>
          <TooltipContent side="bottom" align="end">Arrow at end</TooltipContent>
        </Tooltip>
      </div>
    </TooltipProvider>
  );
}

View code

Tooltip · Icon trigger

Installation

pnpm dlx velaui add @velaui/tooltip
pnpm dlx velaui add @velaui/tooltip

Code

tooltip-icon-trigger.tsx
import { Plus01 } from "@/components/icons/plus-01";
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="outline" size="icon-md" aria-label="Add item">
            <Plus01 />
          </Button>
        </TooltipTrigger>
        <TooltipContent variant="accent" side="right">Add item</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}
import { Plus01 } from "@/components/icons/plus-01";
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger
          render={
            <Button variant="outline" size="icon-md" aria-label="Add item">
              <Plus01 />
            </Button>
          }
        />
        <TooltipContent variant="accent" side="right">Add item</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}

Usage

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

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>
          <Button variant="outline">Hover me</Button>
        </TooltipTrigger>
        <TooltipContent>Add to library</TooltipContent>
      </Tooltip>
    </TooltipProvider>
  );
}
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

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

Props

Tooltip

Takes every prop of TooltipProps.

TooltipContent

PropTypeDefault
arrowPaddingnumber8
sideOffsetnumber4
variant"neutral" | "accent""neutral"

Takes these and every prop of TooltipContentProps.

TooltipProvider

PropTypeDefault
delayDurationnumber0

Takes these and every prop of TooltipProviderProps.

TooltipTrigger

Takes every prop of TooltipTriggerProps.

Tooltip

Takes every prop of TooltipProps.

TooltipContent

PropTypeDefault
alignimport("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").Align"center"
alignOffsetnumber | import("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").OffsetFunction0
arrowPaddingnumber8
sideimport("/app/node_modules/.bun/@base-ui+react@1.6.0+df7f6a806acd27ca/node_modules/@base-ui/react/utils/useAnchorPositioning").Side"top"
sideOffsetnumber4
variant"neutral" | "accent""neutral"

Takes these and every prop of TooltipContentProps.

TooltipProvider

PropTypeDefault
delaynumber0

Takes these and every prop of TooltipProviderProps.

TooltipTrigger

Takes every prop of TooltipTriggerProps.

Dependencies

npm
class-variance-authority@0.7.1radix-ui@1.6.0
Installs with it
nothing else
npm
@base-ui/react@1.6.0class-variance-authority@0.7.1
Installs with it
nothing else

Source

components/ui/tooltip.tsx
/* eslint-disable react-refresh/only-export-components */
"use client";

import * as React from "react";
import { Tooltip as TooltipPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const tooltipContentVariants = cva(
  "group/tooltip z-50 flex w-fit max-w-[240px] origin-(--radix-tooltip-content-transform-origin) flex-col items-start justify-center gap-1.5 rounded-lg px-2 py-1 text-xs leading-[1.3] font-medium has-data-[slot=kbd]:pr-1 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-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
  {
    variants: {
      variant: {
        neutral: "bg-bg-foreground text-text-inverse",
        accent: "bg-accent-default text-text-strong",
      },
    },
    defaultVariants: {
      variant: "neutral",
    },
  },
);

export type TooltipProviderProps = React.ComponentProps<typeof TooltipPrimitive.Provider>;

function TooltipProvider({
  delayDuration = 0,
  ...props
}: TooltipProviderProps) {
  return (
    <TooltipPrimitive.Provider
      data-slot="tooltip-provider"
      delayDuration={delayDuration}
      {...props}
    />
  );
}

export type TooltipProps = React.ComponentProps<typeof TooltipPrimitive.Root>;

function Tooltip({
  ...props
}: TooltipProps) {
  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}

export type TooltipTriggerProps = React.ComponentProps<typeof TooltipPrimitive.Trigger>;

function TooltipTrigger({
  ...props
}: TooltipTriggerProps) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}

export type TooltipContentProps = React.ComponentProps<
  typeof TooltipPrimitive.Content
> &
  VariantProps<typeof tooltipContentVariants>;

function TooltipContent({
  className,
  sideOffset = 4,
  // The design insets the arrow 8px from the start/end edge of the bubble.
  arrowPadding = 8,
  variant = "neutral",
  children,
  ...props
}: TooltipContentProps) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Content
        data-slot="tooltip-content"
        data-variant={variant}
        sideOffset={sideOffset}
        arrowPadding={arrowPadding}
        className={cn(tooltipContentVariants({ variant }), className)}
        {...props}
      >
        {children}
        <TooltipPrimitive.Arrow
          width={12}
          height={6}
          className="z-50 fill-bg-foreground group-data-[variant=accent]/tooltip:fill-accent-default"
        />
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  );
}

export {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
  tooltipContentVariants,
};
components/ui/tooltip.tsx
/* eslint-disable react-refresh/only-export-components */
"use client";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const tooltipContentVariants = cva(
  "group/tooltip z-50 flex w-fit max-w-[240px] origin-(--transform-origin) flex-col items-start justify-center gap-1.5 rounded-lg px-2 py-1 text-xs leading-[1.3] font-medium has-data-[slot=kbd]:pr-1 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-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
  {
    variants: {
      variant: {
        neutral: "bg-bg-foreground text-text-inverse",
        accent: "bg-accent-default text-text-strong",
      },
    },
    defaultVariants: {
      variant: "neutral",
    },
  },
);

export type TooltipProviderProps = TooltipPrimitive.Provider.Props;

function TooltipProvider({
  delay = 0,
  ...props
}: TooltipProviderProps) {
  return (
    <TooltipPrimitive.Provider
      data-slot="tooltip-provider"
      delay={delay}
      {...props}
    />
  );
}

export type TooltipProps = TooltipPrimitive.Root.Props;

function Tooltip({ ...props }: TooltipProps) {
  return <TooltipPrimitive.Root data-slot="tooltip" {...props} />;
}

export type TooltipTriggerProps = TooltipPrimitive.Trigger.Props;

function TooltipTrigger({ ...props }: TooltipTriggerProps) {
  return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
}

export type TooltipContentProps = TooltipPrimitive.Popup.Props &
  Pick<
    TooltipPrimitive.Positioner.Props,
    "align" | "alignOffset" | "side" | "arrowPadding"
  > &
  VariantProps<typeof tooltipContentVariants> & {
    /** Distance from the trigger to the arrow's tip, in px (Radix's meaning). */
    sideOffset?: number;
  };

const ARROW_HEIGHT = 6;

function TooltipContent({
  className,
  side = "top",
  align = "center",
  alignOffset = 0,
  sideOffset = 4,
  // The design insets the arrow 8px from the start/end edge of the bubble.
  arrowPadding = 8,
  variant = "neutral",
  children,
  ...props
}: TooltipContentProps) {
  return (
    <TooltipPrimitive.Portal>
      <TooltipPrimitive.Positioner
        side={side}
        align={align}
        alignOffset={alignOffset}
        // Radix counts the arrow into the offset; Base UI measures from the bubble's edge.
        sideOffset={sideOffset + ARROW_HEIGHT}
        arrowPadding={arrowPadding}
        className="isolate z-50"
      >
        <TooltipPrimitive.Popup
          data-slot="tooltip-content"
          data-variant={variant}
          className={cn(tooltipContentVariants({ variant }), className)}
          {...props}
        >
          {children}
          {/* Radix's arrow: a 12 x 6 triangle hung off the bubble's edge, turned to face the trigger. */}
          <TooltipPrimitive.Arrow className="z-50 h-1.5 w-3 data-[side=bottom]:top-0 data-[side=bottom]:origin-[center_0] data-[side=bottom]:rotate-180 data-[side=left]:right-0 data-[side=left]:origin-[100%_0] data-[side=left]:[transform:translateY(50%)_rotate(-90deg)_translateX(50%)] data-[side=right]:left-0 data-[side=right]:origin-[0_0] data-[side=right]:[transform:translateY(50%)_rotate(90deg)_translateX(-50%)] data-[side=top]:bottom-0 data-[side=top]:translate-y-full">
            <svg
              width={12}
              height={ARROW_HEIGHT}
              viewBox="0 0 30 10"
              preserveAspectRatio="none"
              className="block fill-bg-foreground group-data-[variant=accent]/tooltip:fill-accent-default"
            >
              <polygon points="0,0 30,0 15,10" />
            </svg>
          </TooltipPrimitive.Arrow>
        </TooltipPrimitive.Popup>
      </TooltipPrimitive.Positioner>
    </TooltipPrimitive.Portal>
  );
}

export {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
  tooltipContentVariants,
};