Components

Icon Button

A button component designed specifically for displaying icons

Components

Icon Button

A button component designed specifically for displaying icons

Overview

A button component designed specifically for displaying icons. Perfect for toolbars, action menus, and compact interfaces where space is at a premium.

When to use

Use when space is limited and the icon is unambiguous

Toolbars, table row actions, header bars, compact data interfaces. The icon must clearly represent the action — universal icons only (close, delete, edit, share, search).

Always provide aria-label

Without a visible label, screen readers need aria-label to announce what the button does. The TypeScript type requires it.

Pair icon + text when the action isn't universally recognised

If users could plausibly misread the icon, use a regular Button with leftIcon instead.

Don't use for primary CTAs

The main "Save", "Submit", "Continue" should be a labelled Button. Reserve IconButton for secondary or repeat actions.

Don't pass an icon as children

Use the icon prop, not <IconButton><MdEdit /></IconButton>. The component infers sizing and a11y from the prop.

Basic Usage

Simple Icon Button

Basic icon button with primary variant

<IconButton 
variant="primary" 
icon={<MdAdd />} 
aria-label="Add item" 
/>

Variants

All Variants

Different visual styles for different contexts

<div className="flex items-center gap-3">
<IconButton variant="primary" icon={<MdAdd />} aria-label="Add" />
<IconButton variant="secondary" icon={<MdEdit />} aria-label="Edit" />
<IconButton variant="ghost" icon={<MdShare />} aria-label="Share" />
<IconButton variant="destructive" icon={<MdDelete />} aria-label="Delete" />
<IconButton variant="link" icon={<MdShare />} aria-label="Share" />
</div>

Sizes

SizeButtonIcon
xs24×24px12×12px
sm32×32px16×16px
md36×36px20×20px
lg48×48px24×24px

Icon Button Sizes

Four size options from xs to lg

<div className="flex items-end gap-4">
<IconButton size="xs" variant="secondary" icon={<MdAdd />} aria-label="Add" />
<IconButton size="sm" variant="secondary" icon={<MdAdd />} aria-label="Add" />
<IconButton size="md" variant="secondary" icon={<MdAdd />} aria-label="Add" />
<IconButton size="lg" variant="secondary" icon={<MdAdd />} aria-label="Add" />
</div>

In a Button Group

When placed inside a ButtonGroup, IconButton inherits the group's size automatically via context. You only need to set size directly if you want to override the group's value.

Size from ButtonGroup

IconButton inherits size from its ButtonGroup parent

<div className="flex flex-col gap-4 items-start">
<ButtonGroup size="sm" attached>
  <IconButton variant="secondary" icon={<MdFormatBold />} aria-label="Bold" />
  <IconButton variant="secondary" icon={<MdFormatItalic />} aria-label="Italic" />
  <IconButton variant="secondary" icon={<MdFormatUnderlined />} aria-label="Underline" />
</ButtonGroup>

<ButtonGroup size="lg" attached>
  <IconButton variant="secondary" icon={<MdFormatBold />} aria-label="Bold" />
  <IconButton variant="secondary" icon={<MdFormatItalic />} aria-label="Italic" />
  <IconButton variant="secondary" icon={<MdFormatUnderlined />} aria-label="Underline" />
</ButtonGroup>
</div>

Loading State

Loading Icon Buttons

Show progress with loading state

<div className="flex items-center gap-3">
		<IconButton variant="primary" loading aria-label="Refreshing" />
		<IconButton variant="secondary" loading aria-label="Loading" />
		<IconButton variant="ghost" loading aria-label="Processing" />
</div>

Disabled State

Disabled Icon Buttons

Disabled state across variants

<div className="flex items-center gap-3">
<IconButton variant="primary" icon={<MdAdd />} disabled aria-label="Add (disabled)" />
<IconButton variant="secondary" icon={<MdEdit />} disabled aria-label="Edit (disabled)" />
<IconButton variant="ghost" icon={<MdShare />} disabled aria-label="Share (disabled)" />
<IconButton variant="destructive" icon={<MdDelete />} disabled aria-label="Delete (disabled)" />
</div>

Toolbar Example

Navigation Bar

Icon buttons in a navigation context

<div className="flex items-center justify-between p-3 border rounded-lg bg-panel">
<div className="flex items-center gap-1">
  <IconButton variant="ghost" size="sm" icon={<MdMenu />} aria-label="Menu" />
  <IconButton variant="ghost" size="sm" icon={<MdArrowBack />} aria-label="Back" />
</div>
<div className="flex items-center gap-1">
  <IconButton variant="ghost" size="sm" icon={<MdSearch />} aria-label="Search" />
  <IconButton variant="ghost" size="sm" icon={<MdSettings />} aria-label="Settings" />
  <IconButton variant="ghost" size="sm" icon={<MdMoreVert />} aria-label="More options" />
</div>
</div>

Card Actions

Card with Icon Actions

Icon buttons in a card header

Project Title

Last updated 2 hours ago

<div className="border rounded-lg p-4 bg-panel">
<div className="flex items-start justify-between mb-3">
  <div>
    <h5 className="font-medium">Project Title</h5>
    <p className="text-sm text-ink-light">Last updated 2 hours ago</p>
  </div>
  <div className="flex items-center gap-1">
    <IconButton variant="ghost" size="xs" icon={<MdFavorite />} aria-label="Add to favorites" />
    <IconButton variant="ghost" size="xs" icon={<MdShare />} aria-label="Share" />
    <IconButton variant="ghost" size="xs" icon={<MdMoreVert />} aria-label="More options" />
  </div>
</div>
<div className="flex items-center gap-2">
  <IconButton variant="secondary" size="sm" icon={<MdEdit />} aria-label="Edit project" />
  <IconButton variant="destructive" size="sm" icon={<MdDelete />} aria-label="Delete project" />
</div>
</div>

Form Context

Search Input with Actions

Icon buttons within form inputs

<div className="flex items-center gap-2 p-3 border rounded-lg">
<input
  type="text"
  placeholder="Search..."
  className="flex-1 outline-none bg-transparent"
/>
<IconButton variant="ghost" size="sm" icon={<MdSearch />} aria-label="Search" />
<IconButton variant="ghost" size="sm" icon={<MdClose />} aria-label="Clear" />
</div>

Best Practices

Always provide aria-label

Every icon button must have an aria-label or aria-labelledby attribute to describe its purpose for screen readers.

Use recognizable icons

Choose icons that clearly communicate their action. When in doubt, use text buttons instead.

Size appropriately

Use smaller sizes (xs, sm) for toolbars and compact interfaces. Use larger sizes (md, lg) for primary actions.

Don't use ambiguous icons

Avoid icons that users might not understand. If an icon isn't universally recognized, use a text button.

Don't omit aria-label

Never leave an icon button without an accessible label. Screen reader users need to know what the button does.

Don't use for primary actions

Primary page actions should use text buttons for clarity. Reserve icon buttons for secondary or tertiary actions.

Patterns

// Toolbar row of actions
<div className="flex items-center gap-1">
  <IconButton variant="ghost" size="sm" icon={<MdEdit />} aria-label="Edit" />
  <IconButton variant="ghost" size="sm" icon={<MdShare />} aria-label="Share" />
  <IconButton variant="ghost" size="sm" icon={<MdDelete />} aria-label="Delete" />
</div>

// With tooltip for clarity
<Tooltip content="Delete permanently">
  <IconButton variant="destructive" size="sm" icon={<MdDelete />} aria-label="Delete permanently" />
</Tooltip>

// Loading state
<IconButton icon={<MdRefresh />} aria-label="Refresh" loading={isLoading} />

// Destructive icon action
<IconButton variant="ghost-destructive" size="xs" icon={<MdClose />} aria-label="Remove tag" />

Choosing the right alternative

You want…Use this
Labelled action with optional iconButton (with leftIcon / rightIcon prop)
Icon + text in a tight spaceButton size="xs" with leftIcon
Toggle state (active/inactive)Switch (better signal than icon)
Kebab / "more actions" menuIconButton with <MdMoreVert /> inside a Dropdown
Icon-only link to another pageIconButton asChild wrapping a <Link>
Cluster of related icon actionsButtonGroup containing IconButtons

Common Mistakes

// ❌ WRONG — IconButton does NOT accept children as icon
<IconButton aria-label="Add"><MdAdd /></IconButton>
// ✅ CORRECT — pass icon via the `icon` prop
<IconButton icon={<MdAdd />} aria-label="Add" />

// ❌ WRONG — missing aria-label (required for accessibility)
<IconButton icon={<MdEdit />} />
// ✅ CORRECT
<IconButton icon={<MdEdit />} aria-label="Edit item" />

// ❌ WRONG — using Button with icon only (no text)
<Button><MdDelete /></Button>
// ✅ CORRECT — use IconButton for icon-only triggers
<IconButton icon={<MdDelete />} aria-label="Delete" />

// ❌ WRONG — no "rounded" or "circle" prop
<IconButton icon={<MdAdd />} rounded aria-label="Add" />
// ✅ CORRECT — apply via className if needed
<IconButton icon={<MdAdd />} aria-label="Add" className="rounded-full" />

Props

Props

Accessibility

Required: ARIA Label

Icon buttons MUST include an aria-label or aria-labelledby attribute. This is required for screen reader accessibility.

  • Always provide an aria-label describing the button's action
  • Use clear, action-oriented labels ("Add item" not "Plus")
  • Ensure sufficient touch target size (minimum 44x44px on mobile)
  • Provide keyboard focus indicators
  • Consider adding a tooltip for additional context
  • Use appropriate color contrast for the icon
  • Ensure the icon is visible in both light and dark modes