Overlays
Tooltip
A description that appears on hover and on focus.
Import
import { Tooltip } from '@the_viveksingh/vivek-ui'On hover and on focus
Focus matters as much as hover: a tooltip only reachable with a mouse is invisible to keyboard users. The trigger gets aria-describedby, so the text is announced rather than merely drawn.
<Tooltip content="Copied to your clipboard on click">
<Button>Hover or focus me</Button>
</Tooltip>
<Tooltip content="Billed per active seat, per month">
<IconButton variant="ghost" aria-label="About seat pricing">
<InfoIcon />
</IconButton>
</Tooltip>Sides
side is a preference. If the tooltip would leave the viewport it flips, so it never renders off-screen.
<Tooltip content="Above" side="top"><Button variant="outline">Top</Button></Tooltip>
<Tooltip content="To the right" side="right"><Button variant="outline">Right</Button></Tooltip>
<Tooltip content="Below" side="bottom"><Button variant="outline">Bottom</Button></Tooltip>
<Tooltip content="To the left" side="left"><Button variant="outline">Left</Button></Tooltip>Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
children required | ReactElement<TooltipTriggerProps> | — | The trigger. Exactly one element, and it must spread the props it is given - `aria-describedby` has to land on the real interactive element for a screen reader to read the tip, and a wrapper `<span>` cannot stand in for it. A ref is attached to it (composed with any ref it already had) to measure against, with the first pointer or focus event as a fallback - so a child that swallows refs still positions correctly on hover. |
content required | ReactNode | — | The tip. Keep it short and non-interactive: a tooltip is never focusable. |
side | Side | — | Preferred side. Flipped to its opposite when there is no room. Default `'top'`. |
align | Align | — | Cross-axis alignment. Default `'center'`. |
offset | number | — | Gap between trigger and tip, px. Default `8`. |
padding | number | — | Minimum distance kept from the viewport edges, px. Default `8`. |
open | boolean | — | Controlled open state. |
defaultOpen | boolean | — | Initial open state while uncontrolled. Default `false`. |
onOpenChange | (open: boolean) => void | — | — |
openDelay | number | — | Delay before a hover opens the tip, ms. Default `200`. Keyboard focus deliberately ignores this and opens immediately: a delay on hover stops tips flickering as the pointer crosses a toolbar, but a keyboard user has already committed by pressing Tab, and making them wait is just lag. |
closeDelay | number | — | Delay before the tip closes after the pointer leaves, ms. Default `150`. Must stay above zero: it is the window in which the pointer can cross the gap from the trigger onto the tip itself without the tip disappearing underneath it. |
disabled | boolean | — | Never open. For a trigger whose tip is temporarily meaningless. |
container | PortalContainer | — | Where to portal the tip. Defaults to `document.body`. |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.
Rendering
Tooltip declares 'use client' because it needs state, effects or event handlers. Importing it into a Server Component creates a client boundary at this component — everything above it stays on the server.