Tooltip
The Tooltip component surfaces additional information for an element in a small overlay that appears on hover or focus. It supports a title, a scrollable body, a fixed footer, an arrow, and positioning on any side. Every Tooltip must have a TooltipProvider ancestor — typically wrapped around the entire app.
Variations
Hide arrow
Use hideArrow to remove the tooltip arrow.
Vertical scroll
Content scrolls when it exceeds the max height.
Horizontal scroll
Content scrolls when it exceeds the max width.
Both horizontal and vertical scroll
Content scrolls when it exceeds the max height and max width.
Footer
Use the footer prop for content that stays fixed at the bottom when the body scrolls.
Sides
The tooltip can be positioned on any side: top (default), bottom, left, or right.
Alignment
Control tooltip alignment relative to the trigger using align: start, center (default), or end.
Installation
import { Tooltip, TooltipProvider } from "@harnessio/ui/components";
// TooltipProvider typically wraps the entire app.<TooltipProvider> <Tooltip title="Title" content={<p>This is a tooltip</p>}> <Button variant="secondary">Hover me</Button> </Tooltip></TooltipProvider>;Anatomy
A Tooltip wraps a single trigger element and renders its overlay in a portal.
TooltipProvidermust sit above every tooltip — usually once at the app root. It shares delay and hover behaviour across the tooltips beneath it.Tooltiprenders itschildrenas the trigger (viaasChild), so the child must be a single focusable element that forwards its props and ref.- The overlay is portaled and holds up to three regions: an optional
title, the scrollablecontentbody, and an optionalfooterthat stays fixed while the body scrolls. - An arrow points at the trigger unless
hideArrowis set.
The withTooltip HOC
withTooltip adds tooltip support to any component. For it to work, the wrapped component must use forwardRef and spread its props, so the ref and trigger wiring reach the underlying element.
import { withTooltip } from "@harnessio/ui/components";
const CompWithTooltip = withTooltip(YourComp);
// The wrapped component gains an optional `tooltipProps` object// (every Tooltip prop except `children`). When omitted, no tooltip renders.<CompWithTooltip tooltipProps={{ content: "Helpful context" }} />;Best practices
Do
- Keep tooltip content short and supplementary — a tooltip is context, not essential information.
- Attach the tooltip to a focusable trigger so keyboard and screen-reader users can reach it.
- Use
titlefor a short label andcontentfor the detail; reach forfooterwhen a persistent action or link should stay visible as the body scrolls.
Don’t
- Don’t put essential information or the only copy of an action inside a tooltip — it isn’t visible at a glance and may be missed.
- Don’t wrap a non-interactive or non-focusable element as the trigger; the tooltip then can’t be opened by keyboard.
- Don’t rely on rich markup in
title— it only accepts a string. Usecontentfor anything richer.
Accessibility
Keyboard
- Focusing the trigger opens the tooltip; Escape closes it.
- Hovering the trigger opens the tooltip after the
delay; moving the pointer away closes it.
Labeling & semantics
- The trigger is associated with the overlay via
aria-describedby, so assistive tech announces the tooltip as a description of the trigger. - Because
childrenbecomes the trigger (asChild), pass a single focusable element that forwards props and ref — otherwise the accessibility wiring can’t attach.
Properties
TooltipProvider
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| delayDuration | false | number | |
| skipDelayDuration | false | 0 | number |
| disableHoverableContent | false | boolean |
Tooltip
Prop | Required | Default | Type |
|---|---|---|---|
| children | true | ReactNode | |
| content | true | ReactNode | |
| title | false | string | |
| footer | false | ReactNode | |
| side | false | 'top' | 'top' | 'bottom' | 'left' | 'right' |
| align | false | 'center' | 'start' | 'center' | 'end' |
| hideArrow | false | false | boolean |
| sideOffset | false | 2 (6 when hideArrow is true) | number |
| delay | false | 400 | number |
| open | false | boolean | |
| className | false | string |