Tooltip
A short hint that appears on hover or focus, with an optional keyboard shortcut.
'use client';
import { IconButton, Tooltip } from '@akhatmirzo/ui';
import { Copy, Heart, Share2, Trash2 } from 'lucide-react';
export default function TooltipBasic() {
return (
<div className="flex flex-wrap items-center gap-2">
<Tooltip content="Nusxa olish" shortcut={['mod', 'C']}>
<IconButton variant="outline" aria-label="Nusxa olish">
<Copy />
</IconButton>
</Tooltip>
<Tooltip content="Ulashish">
<IconButton variant="outline" aria-label="Ulashish">
<Share2 />
</IconButton>
</Tooltip>
<Tooltip content="Sevimlilarga qo'shish">
<IconButton variant="outline" aria-label="Sevimlilarga qo'shish">
<Heart />
</IconButton>
</Tooltip>
<Tooltip content="O'chirish" shortcut={['Del']}>
<IconButton variant="ghost" aria-label="O'chirish">
<Trash2 />
</IconButton>
</Tooltip>
</div>
);
}
Import
import { Tooltip, TooltipProvider } from '@akhatmirzo/ui';When to use
- To explain an icon button — the most common case: put the action into words and show its
shortcut (
⌘C). - Short text only. No buttons or links inside — for interactive content, use a
Popover. - Don't hide essential information. Tooltips never show on phones, so anything users must know belongs on the page itself.
- Not a replacement for
aria-label. Icon buttons still need anaria-label; the tooltip is an extra for sighted users.
TooltipProvider
Put one TooltipProvider at the root of your app: all tooltips share the same delay, and moving
from one to the next opens it without a delay (try moving across the buttons above). Tooltips
work without a provider too — each creates its own — you just lose that instant hand-off.
'use client';
import { TooltipProvider } from '@akhatmirzo/ui';
import type { ReactNode } from 'react';
export function Providers({ children }: { children: ReactNode }) {
return <TooltipProvider delay={300}>{children}</TooltipProvider>;
}Sides
side is top (default), right, bottom or left. Near the edge of the screen, the tooltip
flips to the opposite side.
'use client';
import { Button, Tooltip } from '@akhatmirzo/ui';
export default function TooltipSides() {
return (
<div className="flex flex-wrap items-center gap-3">
{(['top', 'right', 'bottom', 'left'] as const).map((side) => (
<Tooltip key={side} content={`Tomon: ${side}`} side={side}>
<Button variant="outline">{side}</Button>
</Tooltip>
))}
</div>
);
}
Delay and long text
delay is how long to wait before opening, in ms. By default it comes from the provider (300).
Text longer than 240px wraps to the next line — but a tooltip is best kept to a line or two.
'use client';
import { Button, Tooltip } from '@akhatmirzo/ui';
export default function TooltipDelay() {
return (
<div className="flex flex-wrap items-center gap-3">
<Tooltip content="Darhol chiqadi" delay={0}>
<Button>delay=0</Button>
</Tooltip>
<Tooltip content="Bir soniya kutadi" delay={1000}>
<Button>delay=1000</Button>
</Tooltip>
<Tooltip content="Uzun izoh 240px dan oshsa, keyingi qatorga o'raladi: buyurtma holati har 5 daqiqada yangilanadi.">
<Button>Uzun matn</Button>
</Tooltip>
</div>
);
}
On a disabled button
A disabled button doesn't fire events, so a tooltip can't attach to it directly. Wrap the
button in a span with tabIndex={0} — keyboard users get the explanation too.
'use client';
import { Button, Tooltip } from '@akhatmirzo/ui';
export default function TooltipDisabledButton() {
return (
<Tooltip content="Ruxsat yo'q: faqat administrator saqlay oladi">
<span tabIndex={0} className="focus-ring inline-flex rounded-lg">
<Button variant="primary" disabled>
Saqlash
</Button>
</span>
</Tooltip>
);
}
Controlled and disabled
Use open + onOpenChange to open the tooltip from code. disabled turns the tooltip off for a
while and leaves the trigger as is. An empty content renders no tooltip either.
'use client';
import { Button, Tooltip } from '@akhatmirzo/ui';
import { useState } from 'react';
export default function TooltipControlled() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-wrap items-center gap-3">
<Button variant="outline" onClick={() => setOpen((o) => !o)}>
{open ? 'Yashirish' : "Ko'rsatish"}
</Button>
<Tooltip content="Boshqariladigan tooltip" open={open} onOpenChange={setOpen}>
<Button>Nishon</Button>
</Tooltip>
<Tooltip content="Bu matn chiqmaydi" disabled>
<Button>disabled</Button>
</Tooltip>
</div>
);
}
Props
Tooltip
The rest of the Radix Tooltip.Content props (alignOffset, avoidCollisions,
onEscapeKeyDown ...) and className go to the tooltip panel.
| Prop | Type | Default | Description |
|---|---|---|---|
contentrequired | ReactNode | — | Short text. When empty, no tooltip is shown |
childrenrequired | ReactElement | — | The trigger — a single element that accepts a ref and events |
side | 'top' | 'right' | 'bottom' | 'left' | 'top' | Preferred side |
align | 'start' | 'center' | 'end' | 'center' | Alignment along the trigger |
sideOffset | number | 6 | Gap to the trigger, px |
collisionPadding | number | 8 | Minimum distance from the screen edge, px |
delay | number | — | Open delay in ms (defaults to the provider, or 300) |
shortcut | string[] | — | A Kbd next to the text: ['mod', 'C'] |
open | boolean | — | Controlled mode |
defaultOpen | boolean | false | Open on first render |
onOpenChange | (open: boolean) => void | — | Called when it opens or closes |
disabled | boolean | false | Turns the tooltip off |
TooltipProvider
Also available as Tooltip.Provider.
| Prop | Type | Default | Description |
|---|---|---|---|
delay | number | 300 | Open delay, ms |
skipDelay | number | 300 | Within this window, the next tooltip opens instantly, ms |
childrenrequired | ReactNode | — | Your app |
Data attributes
| Attribute | On | Value |
|---|---|---|
data-state | panel | delayed-open · instant-open · closed |
data-side | panel | top · right · bottom · left |
data-slot | panel | tooltip |
Keyboard
| Key | Action |
|---|---|
Tab (focus) | The tooltip shows when the trigger is focused |
Esc | Closes the tooltip |
Accessibility
- Wiring. While open, the trigger points to the tooltip text with
aria-describedby, so screen readers read the hint after the button's name. - Shows on keyboard focus, not only on hover.
- Touch devices (no hover, coarse pointer) never open tooltips — a tap just activates the button.
- Color. Tooltips use inverted colors (
bg-text,text-background), so contrast holds in both light and dark mode. - Reduced motion. With
prefers-reduced-motion, only a fade is used.