Icon Button
An icon button is the Button component rendered with only an icon and no text label. Set the iconOnly prop to optimize the button’s styling for a single icon.
Because an icon button has no visible label, you must provide tooltipProps so the action stays accessible. If you intentionally want to skip the tooltip, set ignoreIconOnlyTooltip.
Default Icon Button
Installation
import { Button, IconV2 } from '@harnessio/ui/components'
// Icon only button — provide tooltipProps for accessibility<Button iconOnly tooltipProps={{ content: 'Add' }}> <IconV2 name="plus" /></Button>
// Skip the tooltip explicitly<Button variant="secondary" size="sm" iconOnly ignoreIconOnlyTooltip> <IconV2 name="git-branch" /></Button>
// The 2xs and 3xs sizes set iconOnly automatically<Button variant="outline" size="2xs" ignoreIconOnlyTooltip> <IconV2 name="check" /></Button>Variants and Sizes
Icon buttons support the same variants and sizes as a regular Button. The 2xs and 3xs sizes enable iconOnly automatically.
AI Icon Button
The ai variant pairs with iconOnly for AI-related actions, including the automatic loading state from a promise onClick.
Anatomy
An icon button is the Button rendered with iconOnly, so it’s composed of:
- Container — the button itself, styled for a single icon (square, centered) via
iconOnly. It takes the samevariant,theme, andsizeas any button. - Icon — one
IconV2as the button’s only child, carrying the meaning of the action. - Tooltip — the short label surfaced on hover and focus from
tooltipProps, since there’s no visible text. - Loading spinner (optional) — replaces the icon while
loadingis set (or while an asynconClickis pending).
The 2xs and 3xs sizes turn on iconOnly automatically.
Best practices
Do
- Use icon buttons for common, universally-recognized actions — close, add, edit, delete, search — where the icon alone is unambiguous.
- Always give the action a label via
tooltipProps(and see Accessibility below foraria-label), so it’s identifiable by sight and by assistive tech. - Reach for icon buttons in dense or repeated spots — toolbars, table rows, card corners — where a full text button would crowd the layout.
- Pick an icon whose meaning is obvious for the action; when in doubt, use a regular button with a text label instead.
Don’t
- Don’t use an icon button for rare or ambiguous actions where the icon isn’t self-evident — the tooltip only appears on hover/focus, so the meaning is hidden until then.
- Don’t set
ignoreIconOnlyTooltipjust to quiet the type error; only skip the tooltip when an adjacent visible label already names the action. - Don’t place more than one icon in a single button, and don’t pair an icon button with a nearby text button for the same action.
Content
- Give every icon button a label — in
tooltipProps.content(andaria-label) — that names the action with a verb: “Add”, “Copy link”, “Delete”. - Use sentence case; capitalize only the first word, along with any proper nouns or product names.
- Keep it to the action itself, and write the same words you’d use on a text button for the action.
Accessibility
An icon button has no visible text, so its accessible name has to come from somewhere other than its content — this is the main thing to get right.
Keyboard
- It’s a native
<button>: a tab stop that activates with Enter and Space. - The tooltip opens on focus (not just hover), so keyboard users see the label when they reach the button.
Labeling
tooltipPropsshows a visible label and, through the underlying Radix tooltip, associates that text as the button’s description (aria-describedby) while it’s focused.- For a reliable accessible name, also pass
aria-label(it’s forwarded straight to the<button>); match it to the tooltip text. - The icon itself carries no text, so it should not be the only source of meaning.
Focus & states
- The standard keyboard focus ring (
:focus-visible) shows via the--cn-focustoken. loadinganddisabledboth set thedisabledattribute, removing the button from the tab order while inactive.
Known limitations
- A tooltip is a description, not a name. Even though the types require
tooltipPropsfor icon-only buttons, the component doesn’t turn that text into anaria-label— Radix exposes it as a description, and tooltips aren’t a substitute for an accessible name. For anything important, add an explicitaria-label. ignoreIconOnlyTooltipremoves the label entirely. A button using it has neither a visible tooltip nor a programmatic name unless you supplyaria-label, so reserve it for buttons whose action is already named by an adjacent visible label.- Loading isn’t announced. The loading state sets
data-loadingand disables the button but adds noaria-busyor live region, so screen-reader users aren’t told the action is in progress — same limitation as the base Button.
Properties
Icon buttons use the Button component. These are the props most relevant to icon-only usage — see the Button page for the full API.
Prop | Required | Default | Type |
|---|---|---|---|
| iconOnly | false | false | boolean |
| tooltipProps | false | undefined | Pick<TooltipProps, 'title' | 'content' | 'side'> |
| ignoreIconOnlyTooltip | false | undefined | boolean |
| variant | false | 'primary' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'transparent' |
| theme | false | 'default' | 'default' | 'success' | 'danger' | 'warning' |
| size | false | 'md' | 'md' | 'sm' | 'xs' | '2xs' | '3xs' |
| rounded | false | false | boolean |
| loading | false | false | boolean |
| disabled | false | false | boolean |
| children | true | ReactNode |