Skip to content

Icon button

An icon button runs an action with just an icon, like close, copy or more.

Variant
Size
State
JSX
<IconButton label="More actions"><IconDots /></IconButton>
  • For actions people know by their icon: close, back, copy, share, refresh, more.
  • When space is tight and the same action repeats, like copy and explorer links on every address row.
  • If people would have to guess what the icon does, use a Button with a label.
  • For the main action in a view, use a primary Button. Icon buttons are never primary.
  • To pick one of a few options, even with icons, use a Segmented control.
  • To turn a setting on or off, use a Switch.
  1. 1ContainerA circle, 32 or 40px. Secondary has a fill and border; ghost has neither until hover.
  2. 2Icon20px on small, 24px on medium. Always a 1.67px line.
secondary
ghost
  • Secondary (the default) has a fill and a border. Use it when the button sits on its own, like a refresh button in a header.
  • Ghost shows a fill only on hover. Use it in rows, dialog headers and groups, where many buttons sit close together.

The fills are the same as Button’s secondary and ghost. The icon rests at icon/secondary, a step quieter than a label, and turns icon/primary on hover, press and while its menu is open.

Small, 32px, 20px icon
Medium, 40px, 24px icon
  • Small (32px, 20px icon) fits cards and dialog headers. In dense rows, like copy and explorer actions beside an address, give the icon className="size-4" for a 16px icon.
  • Medium (40px, 24px icon) is the default, for page headers and toolbars.

Icon buttons that sit side by side should be the same size and variant. Every icon draws the same 1.67px line at any size, matching Button.

IconButtonGroup keeps related actions together in one pill. Buttons inside it are small and ghost unless you set otherwise. Give the group a label. Each button is its own Tab stop.

Ghost, small: copy and explorer actions at the end of an address row.

Try the playground to see hover, pressed and focus. A pressed icon button shrinks slightly, to 95%, unless the user has turned on reduced motion. That’s a little more than Button’s 97%, which is too small to feel at 32px.

When an action finishes in place, like copy, swap the icon to a check for a moment. Wrap the icons in IconSwap so the old one shrinks away and the new one grows in, and use DrawnCheck so the check strokes itself in. Neither animates on first render, and with reduced motion the swap is a plain fade.

<IconButton label="Copy token address" onClick={() => copy(address)}>
<IconSwap swapKey={copied ? "check" : "copy"}>
{copied ? <DrawnCheck className="text-positive" /> : <IconCopy />}
</IconSwap>
</IconButton>

Disable an icon button only when you have to, and say why nearby. Hiding an action that never applies is often clearer.

  • The label names the action, not the icon: Close dialog, not X.
  • Use sentence case and no full stop. Name the thing when it helps: Copy token address is clearer than Copy.
  • Use Tabler icons at stroke 2, the same icon for the same action everywhere.
Dolabel="Close dialog" names the action.
Don'tlabel="X icon" names the picture. A screen reader says "X icon, button".
DoAn icon people already know: copy, close, share, more.
Don'tAn action people have to guess. Use a Button with a label.
Key Action
Tab Moves focus to the icon button.
Enter or Space Runs the action.

With asChild and a link, Enter follows the link and Space doesn’t, as for any link.

  • label is required and becomes the aria-label. It’s the only name screen readers get.
  • The label also shows in a Tooltip on hover and keyboard focus. Touch screens never show it, so still pick icons people already know.
  • For a button that toggles, like show and hide, set aria-pressed or change the label with the state, never both.
  • Keyboard focus shows the same 2px ring with a 2px gap as Button. Don’t remove it.
Prop Type Default Notes
label string (required) The accessible name.
variant "secondary" | "ghost" "secondary" "ghost" inside an IconButtonGroup.
size "sm" | "md" "md" "sm" inside an IconButtonGroup.
asChild boolean false Gives the child, usually a link, the icon button’s styles.
tooltip boolean true Shows the label in a Tooltip. Turn off when the same words sit beside the button.
tooltipSide "top" | "right" | "bottom" | "left" "top" Where the tooltip opens.

Every other <button> prop passes through, including disabled, onClick and aria-pressed. IconButtonGroup takes a required label and passes other props to its <div>.