Tooltip
A short text hint for a control that opens on hover after a delay and on keyboard focus, and closes with Escape, on Radix, Base UI or React Aria.
Not yet available: this combination has no tested variant yet. Pick another option above.
Install
Item tooltip-radix · registry JSON
npx shadcn@latest add https://labs.designfrom.com/r/tooltip-radix.jsonnpx shadcn@latest add @designfrom/tooltip-radixOnce, in components.json: "registries": { "@designfrom": "https://labs.designfrom.com/r/{name}.json" }
npx designfrom add tooltipnpm i @designfrom/reactimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@designfrom/react/radix/tooltip";Source
Comments are kept. The header lists provenance, license and install commands for people and coding assistants.
"use client";
import { Tooltip as TooltipPrimitive } from "radix-ui";
import type * as React from "react";
import { cn } from "@/lib/utils";
/**
* @dfl-header: v1, generated by labs.designfrom.com at build time
* Item: tooltip-radix (Tooltip: Radix)
* What: A short text hint for a control that opens on hover after a delay
* and on keyboard focus, and closes with Escape, on Radix, Base UI or
* React Aria.
* Status: ready. License: MIT. Author: Design From Labs
* <https://labs.designfrom.com>
* Source: https://github.com/Design-from-labs/labs @ 803b92c1a30e (examples/tooltip)
* Credits: S-003 React (MIT); S-004 shadcn/ui and its registry schema (MIT);
* S-008 Tailwind CSS (MIT); S-027 WAI-ARIA Authoring Practices:
* Tooltip pattern (W3C Software and Document License); S-032 Radix
* Primitives (the unified radix-ui package) (MIT); S-033 Base UI
* (@base-ui/react) (MIT); S-034 React Aria Components
* (react-aria-components) (Apache-2.0)
* Install: npx shadcn@latest add https://labs.designfrom.com/r/tooltip-radix.json
* npx shadcn@latest add @designfrom/tooltip-radix
* npx designfrom add tooltip
* npm i @designfrom/react, then import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@designfrom/react/radix/tooltip";
* Requires: radix-ui, registry: utils
* Docs: https://labs.designfrom.com/ui/tooltip (index: https://labs.designfrom.com/llms.txt)
*/
// Wrap the app (or a section) once; it shares the open delay between
// tooltips so moving across a toolbar does not wait again.
function TooltipProvider({
delayDuration = 300,
...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 = 6,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
// Fades in from transparent on mount (@starting-style); opacity
// only, so it stays on the compositor. No motion when reduced.
"z-50 w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-background text-xs leading-snug shadow-md transition-opacity duration-150 starting:opacity-0 motion-reduce:transition-none",
className,
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="fill-foreground" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
}
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };Dependencies
- radix-ui@^1
- utils
Install
Item tooltip-base · registry JSON
npx shadcn@latest add https://labs.designfrom.com/r/tooltip-base.jsonnpx shadcn@latest add @designfrom/tooltip-baseOnce, in components.json: "registries": { "@designfrom": "https://labs.designfrom.com/r/{name}.json" }
npx designfrom add tooltip --primitive basenpm i @designfrom/reactimport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@designfrom/react/base/tooltip";Source
Comments are kept. The header lists provenance, license and install commands for people and coding assistants.
"use client";
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
/**
* @dfl-header: v1, generated by labs.designfrom.com at build time
* Item: tooltip-base (Tooltip: Base UI)
* What: A short text hint for a control that opens on hover after a delay
* and on keyboard focus, and closes with Escape, on Radix, Base UI or
* React Aria.
* Status: ready. License: MIT. Author: Design From Labs
* <https://labs.designfrom.com>
* Source: https://github.com/Design-from-labs/labs @ 803b92c1a30e (examples/tooltip)
* Credits: S-003 React (MIT); S-004 shadcn/ui and its registry schema (MIT);
* S-008 Tailwind CSS (MIT); S-027 WAI-ARIA Authoring Practices:
* Tooltip pattern (W3C Software and Document License); S-032 Radix
* Primitives (the unified radix-ui package) (MIT); S-033 Base UI
* (@base-ui/react) (MIT); S-034 React Aria Components
* (react-aria-components) (Apache-2.0)
* Install: npx shadcn@latest add https://labs.designfrom.com/r/tooltip-base.json
* npx shadcn@latest add @designfrom/tooltip-base
* npx designfrom add tooltip --primitive base
* npm i @designfrom/react, then import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@designfrom/react/base/tooltip";
* Requires: @base-ui/react, registry: utils
* Docs: https://labs.designfrom.com/ui/tooltip (index: https://labs.designfrom.com/llms.txt)
*/
// Wrap the app (or a section) once; it shares the open delay between
// tooltips so moving across a toolbar does not wait again.
function TooltipProvider({
delay = 300,
...props
}: TooltipPrimitive.Provider.Props) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delay={delay}
{...props}
/>
);
}
// Base UI treats tooltips as visual only. To follow the WAI-ARIA tooltip
// pattern (role=tooltip, described by from the trigger), the root shares one
// id between the trigger and the popup.
const TooltipIdContext = React.createContext<string | undefined>(undefined);
function Tooltip(props: TooltipPrimitive.Root.Props) {
const id = React.useId();
return (
<TooltipIdContext.Provider value={id}>
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
</TooltipIdContext.Provider>
);
}
function TooltipTrigger(props: TooltipPrimitive.Trigger.Props) {
const id = React.useContext(TooltipIdContext);
return (
<TooltipPrimitive.Trigger
data-slot="tooltip-trigger"
aria-describedby={id}
{...props}
/>
);
}
function TooltipContent({
className,
side = "top",
sideOffset = 6,
align = "center",
children,
...props
}: Omit<TooltipPrimitive.Popup.Props, "className"> & {
className?: string;
} & Pick<TooltipPrimitive.Positioner.Props, "side" | "sideOffset" | "align">) {
const id = React.useContext(TooltipIdContext);
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Positioner
side={side}
sideOffset={sideOffset}
align={align}
className="isolate z-50"
>
<TooltipPrimitive.Popup
data-slot="tooltip-content"
role="tooltip"
id={id}
className={cn(
// Base UI marks the first and last frame with data attributes,
// so a plain opacity transition animates both open and close.
"w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-background text-xs leading-snug shadow-md transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 motion-reduce:transition-none",
className,
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="size-2.5 rotate-45 rounded-[2px] bg-foreground data-[side=bottom]:-top-1 data-[side=top]:-bottom-1" />
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
);
}
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };Dependencies
- @base-ui/react@^1
- utils
Install
Item tooltip-aria · registry JSON
npx shadcn@latest add https://labs.designfrom.com/r/tooltip-aria.jsonnpx shadcn@latest add @designfrom/tooltip-ariaOnce, in components.json: "registries": { "@designfrom": "https://labs.designfrom.com/r/{name}.json" }
npx designfrom add tooltip --primitive arianpm i @designfrom/reactimport { Tooltip, TooltipTrigger } from "@designfrom/react/aria/tooltip";Source
Comments are kept. The header lists provenance, license and install commands for people and coding assistants.
"use client";
import * as React from "react";
import {
Focusable,
OverlayArrow,
Tooltip as TooltipPrimitive,
TooltipTrigger as TooltipTriggerPrimitive,
} from "react-aria-components";
import { cn } from "@/lib/utils";
/**
* @dfl-header: v1, generated by labs.designfrom.com at build time
* Item: tooltip-aria (Tooltip: React Aria)
* What: A short text hint for a control that opens on hover after a delay
* and on keyboard focus, and closes with Escape, on Radix, Base UI or
* React Aria.
* Status: ready. License: MIT. Author: Design From Labs
* <https://labs.designfrom.com>
* Source: https://github.com/Design-from-labs/labs @ 803b92c1a30e (examples/tooltip)
* Credits: S-003 React (MIT); S-004 shadcn/ui and its registry schema (MIT);
* S-008 Tailwind CSS (MIT); S-027 WAI-ARIA Authoring Practices:
* Tooltip pattern (W3C Software and Document License); S-032 Radix
* Primitives (the unified radix-ui package) (MIT); S-033 Base UI
* (@base-ui/react) (MIT); S-034 React Aria Components
* (react-aria-components) (Apache-2.0)
* Install: npx shadcn@latest add https://labs.designfrom.com/r/tooltip-aria.json
* npx shadcn@latest add @designfrom/tooltip-aria
* npx designfrom add tooltip --primitive aria
* npm i @designfrom/react, then import { Tooltip, TooltipTrigger } from "@designfrom/react/aria/tooltip";
* Requires: react-aria-components, registry: utils
* Docs: https://labs.designfrom.com/ui/tooltip (index: https://labs.designfrom.com/llms.txt)
*/
// React Aria pairs the trigger and the tooltip inside TooltipTrigger:
// <TooltipTrigger><button>...</button><Tooltip>...</Tooltip></TooltipTrigger>.
// Focusable gives a plain focusable child the trigger behaviour.
function TooltipTrigger({
delay = 300,
children,
...props
}: React.ComponentProps<typeof TooltipTriggerPrimitive>) {
const [trigger, tooltip] = React.Children.toArray(children);
return (
<TooltipTriggerPrimitive
data-slot="tooltip-trigger"
delay={delay}
{...props}
>
<Focusable>
{trigger as React.ComponentProps<typeof Focusable>["children"]}
</Focusable>
{tooltip}
</TooltipTriggerPrimitive>
);
}
function Tooltip({
className,
placement = "top",
offset = 8,
children,
...props
}: Omit<
React.ComponentProps<typeof TooltipPrimitive>,
"children" | "className"
> & {
className?: string;
children?: React.ReactNode;
}) {
return (
<TooltipPrimitive
data-slot="tooltip-content"
placement={placement}
offset={offset}
className={cn(
// React Aria marks entering and exiting frames, so a plain opacity
// transition animates both open and close.
"z-50 w-fit max-w-xs rounded-md bg-foreground px-3 py-1.5 text-background text-xs leading-snug shadow-md transition-opacity duration-150 data-entering:opacity-0 data-exiting:opacity-0 motion-reduce:transition-none",
className,
)}
{...props}
>
<OverlayArrow>
<svg
width={10}
height={5}
viewBox="0 0 10 5"
aria-hidden="true"
className="block fill-foreground in-data-[placement=bottom]:rotate-180"
>
<path d="M0 0 L5 5 L10 0" />
</svg>
</OverlayArrow>
{children}
</TooltipPrimitive>
);
}
export { Tooltip, TooltipTrigger };Dependencies
Usage
<Tooltip>
<TooltipTrigger>Share</TooltipTrigger>
<TooltipContent>Copies a link</TooltipContent>
</Tooltip>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| TooltipProvider delayDuration / delay | number | 300 | Radix and Base UI: milliseconds of hover before a tooltip opens; shared by every tooltip inside the provider. |
| TooltipContent side | "top" | "right" | "bottom" | "left" | "top" | Radix and Base UI: where the hint appears relative to the trigger. |
| TooltipTrigger delay | number | 300 | React Aria: milliseconds of hover before it opens. |
| Tooltip placement | "top" | "bottom" | "start" | "end" | ... | "top" | React Aria: where the hint appears; start and end follow the reading direction. |
| className | string | Extra classes for the hint bubble. |
Accessibility
- Follows the WAI-ARIA tooltip pattern: role=tooltip, linked to the trigger with aria-describedby.
- Opens on keyboard focus and closes on blur or Escape.
- Keep the hint short and non-interactive; use a popover for links or buttons.
- The fade animates opacity only and is disabled when the user prefers reduced motion.
Credits
- React · Meta Platforms and contributors · MIT
- shadcn/ui and its registry schema · shadcn · MIT
- Tailwind CSS · Tailwind Labs · MIT
- WAI-ARIA Authoring Practices: Tooltip pattern · W3C · W3C Software and Document License
- Radix Primitives (the unified radix-ui package) · WorkOS and contributors · MIT
- Base UI (@base-ui/react) · MUI and contributors · MIT
- React Aria Components (react-aria-components) · Adobe and contributors · Apache-2.0
License and source
- License
- MIT
- Author
- Design From Labs <https://labs.designfrom.com>
- Source
- github.com/Design-from-labs/labs
- Commit
- 803b92c1a30e
- Date
- 2026-10-12
- Origin
- labs-examples