Skip to Content
ComponentsTooltip

Tooltip

A hover/focus tooltip built on Base UI’s Tooltip primitive. Automatically positioned via Floating UI with a scale/fade transition.

Import

import { Button, Tooltip } from "chunks-ui";

Basic Usage

<Tooltip.Root> <Tooltip.Trigger render={<Button variant="outlined" />}>Hover me</Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup>Helpful tip</Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip.Root>

With Arrow

<Tooltip.Root> <Tooltip.Trigger render={<Button variant="outlined" />}>Info</Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup> <Tooltip.Arrow /> This is a tooltip with an arrow. </Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip.Root>

Tooltip Provider

Use Tooltip.Provider to share delay settings across multiple tooltips. Every Tooltip.Trigger inside it opens after the Provider’s delay, and once one tooltip is open, moving to a nearby one opens it instantly:

<Tooltip.Provider delay={200} closeDelay={0}> <Tooltip.Root> <Tooltip.Trigger render={<Button variant="outlined" />}>A</Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup>Tooltip A</Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip.Root> <Tooltip.Root> <Tooltip.Trigger render={<Button variant="outlined" />}>B</Tooltip.Trigger> <Tooltip.Portal> <Tooltip.Positioner> <Tooltip.Popup>Tooltip B</Tooltip.Popup> </Tooltip.Positioner> </Tooltip.Portal> </Tooltip.Root> </Tooltip.Provider>

A delay on a Tooltip.Trigger overrides the Provider’s for that trigger.

Use the Tooltip.Provider from chunks-ui, not the one from @base-ui/react/tooltip: chunks-ui triggers ignore a Base UI Provider’s delay.

A nested Tooltip.Provider without a delay uses 50ms, not the outer Provider’s delay, so repeat the outer delay on it.

Positioning

The popup sits 2 spacing units (8px at the default --spacing) from the trigger. The arrow tip clears the trigger at any spacing. A sideOffset on the positioner adds to that gap. The arrow stays at least arrowPadding pixels from the popup’s ends, clear of the rounded corner. The default is the popup’s corner radius plus 0.21 × the arrow’s size (about 10 in the default theme), read when the popup mounts and again when a Tooltip.Arrow mounts or unmounts in it, so it follows --radius and --spacing but not a later change to them. A keepMounted tooltip mounts closed and keeps that value while it stays mounted; an arrow with a content-sized width (auto, fit-content, a percentage) has no pixel width while hidden, so it gets Base UI’s default until the tooltip mounts on open. With an explicit arrowPadding, or an arrow with a content-sized width, a Tooltip.Arrow that mounts or unmounts while the tooltip is open is placed at the next scroll, resize or open. On the left and right, one line of text (about 24px tall) is too short for that padding, so Floating UI holds the arrow near the middle; there the two corners on the arrow’s edge become elliptical, curving less far along it, so the arrow’s foot still meets a straight edge. They start from --radius-md and ignore rounded-* on Tooltip.Popup, and assume the default arrow (size-2.5). To square them, or to fit a larger arrow, set --tooltip-arrow-corner on Tooltip.Popup: [--tooltip-arrow-corner:0px] with rounded-none squares all four corners, and a length such as [--tooltip-arrow-corner:var(--radius-xl)] sets the two on the arrow’s edge. With align="start" or "end", the popup shifts past a trigger narrower than 30px by up to 5px. Override placement with side and align:

<Tooltip.Positioner side="right" align="center"> <Tooltip.Popup>Right-aligned tooltip</Tooltip.Popup> </Tooltip.Positioner>

Animation

The popup enters with a scale-up and fade (from scale-95 and opacity-0): the popup spring (springs.popup) with Motion installed, a CSS transition of opacity and scale without it; reduced motion turns off both. The popup grows out of its trigger (origin-(--transform-origin)); a className such as origin-top replaces it.

In a Tooltip.Provider, a tooltip that opens while another one is open, or within the Provider’s timeout (400ms by default) of one closing, shows at once, and the other one hides at once. Base UI marks both popups data-instant="delay", and both paths skip the animation. Outside such a swap, a tooltip opened by keyboard focus, or closed with Escape or a click on its trigger, still animates.

render on Tooltip.Popup replaces the element the spring animates, so a popup with render uses the CSS transition even with Motion installed. To restyle the popup, pass className.

Compound Parts

PartDescription
Tooltip.RootState container. Manages hover/focus open state.
Tooltip.TriggerThe element that activates the tooltip on hover/focus. Renders a <button>, so don’t nest another button inside it: pass render={<Button />} to use a chunks-ui Button.
Tooltip.PortalRenders children in a portal.
Tooltip.PositionerFloating UI positioner. z-tooltips (400). No default sideOffset; the gap to the trigger is a 2-spacing-unit margin on Tooltip.Popup, and a sideOffset adds to it. arrowPadding defaults to the popup’s corner radius plus 0.21 × the arrow’s size (about 10).
Tooltip.PopupThe tooltip content. Dark background (bg-foreground), light text (text-background).
Tooltip.ArrowA rotated square (bg-foreground) absolutely positioned at the popup edge. Aligned to the anchor via Base UI CSS variables.
Tooltip.ProviderShared delay configuration for grouped tooltips. delay defaults to 50.

Props

All compound parts are thin style wrappers. Props are forwarded to the underlying Base UI Tooltip. The open delay is 50ms by default (Base UI’s default is 600). A Tooltip.Trigger uses its own delay if set, then the nearest Tooltip.Provider’s delay, then 50.

Last updated on