Overlays
Tooltip.
A dark label in small mono type that appears on hover or keyboard focus. Good for naming icon buttons.
import { CopyIcon, RefreshCwIcon, TerminalSquareIcon, Trash2Icon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
const actions = [
{ label: "Open terminal", icon: TerminalSquareIcon },
{ label: "Copy address", icon: CopyIcon },
{ label: "Reboot computer", icon: RefreshCwIcon },
{ label: "Delete hub", icon: Trash2Icon },
];
export default function TooltipDemo() {
return (
<TooltipProvider>
<div className="flex items-center gap-2">
{actions.map(({ label, icon: Icon }) => (
<Tooltip key={label}>
<TooltipTrigger asChild>
<Button variant="outline" size="icon" aria-label={label}>
<Icon />
</Button>
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
))}
</div>
</TooltipProvider>
);
}Installation#
With the CLI
$npx lanterncn add tooltipThis also installs the Lantern theme and any Lantern UI components it depends on. The shadcn CLI works directly too: npx shadcn@latest add httptim/lantern-ui/tooltip. See Installation if your project is not set up yet.
Manually
Install the dependencies
npm install radix-uiCopy the source into your project
components/ui/tooltip.tsx"use client"; import * as React from "react"; import { Tooltip as TooltipPrimitive } from "radix-ui"; import { cn } from "@/lib/utils"; function TooltipProvider({ delayDuration = 150, ...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 ( <TooltipProvider> <TooltipPrimitive.Root data-slot="tooltip" {...props} /> </TooltipProvider> ); } function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) { return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />; } function TooltipContent({ className, sideOffset = 6, children, ...props }: React.ComponentProps<typeof TooltipPrimitive.Content>) { return ( <TooltipPrimitive.Portal> <TooltipPrimitive.Content data-slot="tooltip-content" sideOffset={sideOffset} className={cn( "z-50 w-fit max-w-[calc(100vw-1.5rem)] origin-(--radix-tooltip-content-transform-origin) rounded-sm border border-[#3a463e] bg-[#0b0f0d] px-2 py-1.5 font-mono text-[10px] leading-snug tracking-[0.12em] text-foreground uppercase", "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95", "data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", className, )} {...props} > {children} <TooltipPrimitive.Arrow className="z-50 fill-[#0b0f0d]" width={8} height={4} /> </TooltipPrimitive.Content> </TooltipPrimitive.Portal> ); } export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };Update the import paths to match your project
The source imports
cnfrom@/lib/utils.
Usage#
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";<Tooltip>
<TooltipTrigger asChild>
<Button size="icon" aria-label="Copy address"><CopyIcon /></Button>
</TooltipTrigger>
<TooltipContent>Copy address</TooltipContent>
</Tooltip>Each Tooltip brings its own provider. Wrap a group in TooltipProvider to share the open delay so moving between buttons shows the next tooltip instantly.
Tooltips do not show on touch screens. Keep an aria-label on icon-only buttons.