Skip to content
Highlight

V2 componentsUpdated Nov 9, 2026

Tooltip

A short label that names a control on hover or keyboard focus, after a 300ms delay. Use it for icon-only buttons and to show its hotkey. Dark in both themes, like Toast, so it reads as chrome rather than part of the page. Never put anything interactive or essential in it: touch users never see it. For rich or clickable content use Preview Card or Popover.

Source: packages/ui/src/components/tooltip

tsx
import { Tooltip } from '@highlight/ui'

Change the props; the preview and the snippet update together.

Props
Side
Align
8
Hotkey
Always open
tsx
<Tooltip>  <Tooltip.Trigger render={<Button>Share</Button>} />  <Tooltip.Content>    Share summary  </Tooltip.Content></Tooltip>

Pass the control through render so the trigger is a single element. Keep its aria-label.

tsx
import { Button, Tooltip } from '@highlight/ui'import { Copy } from 'lucide-react' export default function TooltipBasic() {  return (    <Tooltip>      <Tooltip.Trigger        render={          <Button aria-label="Copy link" shape="square" variant="ghost">            <Copy />          </Button>        }      />      <Tooltip.Content>Copy link</Tooltip.Content>    </Tooltip>  )}

Takes the same "cmd+k" string as Button's hotkey prop and renders the same keycap chips.

tsx
import { Button, Tooltip } from '@highlight/ui'import { Search } from 'lucide-react' export default function TooltipHotkey() {  return (    <Tooltip>      <Tooltip.Trigger        render={          <Button aria-label="Search" shape="square" variant="ghost">            <Search />          </Button>        }      />      <Tooltip.Content>        Search        <Tooltip.Hotkey hotkey="cmd+k" />      </Tooltip.Content>    </Tooltip>  )}

Wrap neighbours in Provider: once one opens, the next opens without the delay. Each keeps its own popup, so use Group for a tight row.

tsx
import { Button, Tooltip } from '@highlight/ui' const SIDES = ['top', 'right', 'bottom', 'left'] as const export default function TooltipSides() {  return (    <Tooltip.Provider>      <div className="flex flex-wrap items-center justify-center gap-3">        {SIDES.map((side) => (          <Tooltip key={side}>            <Tooltip.Trigger              render={                <Button size="sm" variant="outline" className="capitalize">                  {side}                </Button>              }            />            <Tooltip.Content side={side}>Opens on the {side}</Tooltip.Content>          </Tooltip>        ))}      </div>    </Tooltip.Provider>  )}

Wrap a row of icon buttons in Group, e.g. message actions or a toolbar. The tooltips share one popup that moves between triggers and swaps its text, so sliding along the row never closes and reopens it. Tooltips inside keep their usual markup, even in child components.

tsx
import { Button, Tooltip } from '@highlight/ui'import { Copy, Link2, RotateCcw, ThumbsDown, ThumbsUp, Volume2 } from 'lucide-react' const ACTIONS = [  { label: 'Copy message', Icon: Copy },  { label: 'Read aloud', Icon: Volume2 },  { label: 'Retry', Icon: RotateCcw },  { label: 'Copy link', Icon: Link2 },  { label: 'Good response', Icon: ThumbsUp },  { label: 'Bad response', Icon: ThumbsDown },] export default function TooltipGroup() {  return (    <Tooltip.Group>      <div className="flex items-center gap-1.5">        {ACTIONS.map(({ label, Icon }) => (          <Tooltip key={label}>            <Tooltip.Trigger              render={                <Button aria-label={label} size="sm" shape="square" variant="ghost">                  <Icon />                </Button>              }            />            <Tooltip.Content>{label}</Tooltip.Content>          </Tooltip>        ))}      </div>    </Tooltip.Group>  )}

Wraps at 240px. If it needs more than two lines, it belongs on the page.

tsx
import { Button, Tooltip } from '@highlight/ui' export default function TooltipLong() {  return (    <Tooltip>      <Tooltip.Trigger render={<Button variant="outline">Auto-join</Button>} />      <Tooltip.Content>        Highlight joins every meeting on your calendar that has a video link, and leaves when the last person does.      </Tooltip.Content>    </Tooltip>  )}

The root, then any compound sub-components. Every part accepts className and forwards the rest of its props to the element or Base UI part beneath it.

Tooltip
Base UI Tooltip.Root. Owns the open state; holds the trigger and the content.
Tooltip.Provider
Shares the delay across neighbouring tooltips, so the next opens without it. closeDelay defaults to 100ms to bridge gaps.
Tooltip.Group
A Provider whose tooltips share one popup (Base UI multiple triggers). Root props on a Tooltip inside it, like open, are ignored.
Tooltip.Trigger
Unstyled trigger; use render to supply a Button. delay defaults to 300.
Tooltip.Content
side (default top), sideOffset (8), align, alignOffset, collisionAvoidance.
Tooltip.Hotkey
Kbd: hotkey="cmd+k" as keycap chips after the label, the same chips as Button.
Tooltip.Portal / Positioner / Popup
Low-level parts.