Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
Primitives
  • Accordion
  • Alert
  • Alert dialog
  • Aspect ratio
  • Autocomplete
  • Avatar
  • Badge
  • Button
  • Button group
  • Calendar
  • Card
  • Checkbox
  • Checkbox Group
  • Code
  • Code Diff
  • Collapsible
  • Color Picker
  • Combobox
  • Command
  • Date Picker
  • Dialog
  • Drawer
  • Empty
  • Field
  • Form
  • Gradient Editor
  • Hover card
  • Input
  • Input group
  • Input OTP
  • Item
  • Kbd
  • Markdown
  • Menu
  • Menubar
  • Meter
  • Native select
  • Navigation menu
  • Number Field
  • Pagination
  • Popover
  • Progress
  • Radio group
  • Resizable
  • Scroll area
  • Select
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Table of contents
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toolbar
  • Tooltip
  • Tree
  • Trigger Menu
  • Typography
Hooks
  • Use Chat Message
  • Use Chat Input
  • Use User Ask
  • Use Audio Recorder
  • Use Environment Variables
  • Use Copy To Clipboard
  • Use Tool Call
  • Use Sidebar Resize
Utils
  • cn
  • Skin
  • Contracts
  • Serialize
Extensions
  • Control Effects
  • View Transition
  • Send Aurora
  1. Primitives
  2. Tooltip
Primitives

Tooltip

Hover or focus hint popup with Base UI positioning and Refined UI tokens.

Composition

The preferred shape for composing the installed primitive from its exported parts.

Parts

Exported compound parts from the installed source.

TooltipProvider
├── Tooltip
├── TooltipTrigger
└── TooltipContent

The refined source installs this primitive from src/registry/sources/refined/ui/tooltip.tsx. Install it on its own with the command above, or inspect the source below.

Registry command
npx shadcn@latest add http://127.0.0.1:3000/r/refined/tooltip.json
See registry manifest

Raw code

Primary installed primitive source

Base UI Tooltip slot
src/registry/sources/refined/ui/tooltip.tsx
"use client";
​
import type { TooltipPopupProps, TooltipPositionerProps } from "@base-ui/react/tooltip";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import { type ComponentProps, isValidElement, type Ref } from "react";
import { cn } from "@/components/refined-ui/lib/cn";
import { skinEffects, skinId, skinSlot } from "@/components/refined-ui/skin";
​
type TooltipProviderProps = Omit<ComponentProps<typeof TooltipPrimitive.Provider>, "delay" | "timeout"> & {
delay?: number;
timeout?: number;
delayDuration?: number;
skipDelayDuration?: number;
};
​
export function TooltipProvider({ delay, delayDuration, timeout, skipDelayDuration, ...props }: TooltipProviderProps) {
const resolvedDelay = delay ?? delayDuration;
const resolvedTimeout = timeout ?? skipDelayDuration;
​
return (
<TooltipPrimitive.Provider
{...(resolvedDelay !== undefined ? { delay: resolvedDelay } : {})}
{...(resolvedTimeout !== undefined ? { timeout: resolvedTimeout } : {})}
{...props}
/>
);
}
​
export function Tooltip(props: ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root {...props} />;
}
​
type TooltipTriggerProps = ComponentProps<typeof TooltipPrimitive.Trigger> & {
/** @deprecated Use Base UI's `render` prop instead. */
asChild?: boolean;
ref?: Ref<HTMLButtonElement>;
};
​
export function TooltipTrigger({ asChild, render, children, ref, ...props }: TooltipTriggerProps) {
if (asChild && isValidElement(children)) {
return <TooltipPrimitive.Trigger ref={ref} render={children} {...props} />;
}
​
return (
<TooltipPrimitive.Trigger ref={ref} render={render} {...props}>
{children}
</TooltipPrimitive.Trigger>
);
}
​
type TooltipContentPositionerProps = Omit<TooltipPositionerProps, keyof TooltipPopupProps>;
​
type TooltipContentProps = TooltipPopupProps &
TooltipContentPositionerProps & {
arrow?: boolean;
hidden?: boolean;
ref?: Ref<HTMLDivElement>;
};
​
export function TooltipContent({
className,
children,
side = "top",
sideOffset = 8,
align = "center",
alignOffset = 0,
arrowPadding = 10,
anchor,
positionMethod,
collisionBoundary,
collisionPadding,
sticky,
disableAnchorTracking,
collisionAvoidance,
arrow = true,
hidden = false,
ref,
...props
}: TooltipContentProps) {
if (hidden) return null;
​
const positionerProps: TooltipContentPositionerProps = {
side,
sideOffset,
align,
alignOffset,
arrowPadding,
anchor,
positionMethod,
collisionBoundary,
collisionPadding,
sticky,
disableAnchorTracking,
collisionAvoidance,
};
​
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Positioner data-skin={skinId()} data-effects={skinEffects()} className="z-[90] outline-none" {...positionerProps}>
<TooltipPrimitive.Popup
ref={ref}
role="tooltip"
data-ui="agent"
data-slot="tooltip-content"
className={cn(
"relative flex w-fit max-w-xs origin-[var(--transform-origin)] flex-col bg-foreground px-2.5 py-1 text-xs text-balance text-background shadow-pop outline-none transition-[opacity,scale] duration-[var(--duration-fast)] data-[instant]:transition-none data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
!arrow && "rounded-[var(--radius-control)]",
skinSlot("tooltip-content", {}),
arrow && "rounded-[clamp(0px,var(--radius-control),3px)]",
className,
)}
{...props}
>
{children}
{arrow ? (
<TooltipPrimitive.Arrow
className={cn(
"flex text-foreground",
"data-[side=top]:-bottom-[8px] data-[side=top]:rotate-180",
"data-[side=bottom]:-top-[8px]",
"data-[side=left]:-right-[10px] data-[side=left]:rotate-90",
"data-[side=right]:-left-[10px] data-[side=right]:-rotate-90",
)}
>
<TooltipArrowSvg />
</TooltipPrimitive.Arrow>
) : null}
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
);
}
​
function TooltipArrowSvg() {
return (
<svg aria-hidden="true" focusable="false" width="12" height="8" viewBox="0 0 12 8" fill="none" overflow="visible">
<path d="M0 7L4 2Q6 0 8 2L12 7L12 8L0 8Z" className="fill-current" />
</svg>
);
}
​

On this page

  • Preview
  • Composition
  • Installation
  • Source