Kit

ActionIcon

Square, icon-only sibling of Button. Same (variant × color) model plus its own radius scale.

ActionIcon is the icon-only counterpart to Button. It shares the same orthogonal (variant × color) model and adds its own radius scale so circular triggers (toolbars, like buttons, dismiss controls) drop straight in.

import { ActionIcon } from '@42/ui-react/action-icon';
import { Settings } from 'lucide-react';

<ActionIcon aria-label="Open settings">
  <Settings />
</ActionIcon>

The icon is auto-sized by the parent's size variant — drop in a bare lucide-react icon and you'll never need to set size={n} on it.

aria-label is required. The icon children are decoration — screen readers won't announce them. Without a label, the trigger is effectively invisible to assistive tech.

Playground

Component

Presets

Props

Code

<Component  disabled={false}  color="brand"  variant="filled"  size="xs"  radius="sm"  isLoading={false}/>

Full prop browser

If you need to inspect every prop on ActionIconProps (including spread-through HTML attributes), use the kitchen-sink view.

Variants

Five visual treatments, identical in semantics to Button's. Each reads off the same color slots (--c-solid, --c-soft, --c-text) so palette swaps are uniform.

import { Star } from 'lucide-react';

<ActionIcon variant="filled"  aria-label="Star"><Star /></ActionIcon>
<ActionIcon variant="light"   aria-label="Star"><Star /></ActionIcon>
<ActionIcon variant="outline" aria-label="Star"><Star /></ActionIcon>
<ActionIcon variant="subtle"  aria-label="Star"><Star /></ActionIcon>
<ActionIcon variant="default" aria-label="Star"><Star /></ActionIcon>

subtle remains the toolbar workhorse: borderless, no resting fill, hover tint reveals the palette.

Colors

Every variant (except default) accepts any color from the shared palette.

<ActionIcon variant="filled"  color="brand" aria-label="Favorite"><Heart /></ActionIcon>
<ActionIcon variant="light"   color="blue"  aria-label="Info"><Star /></ActionIcon>
<ActionIcon variant="outline" color="teal"  aria-label="Toggle"><Star /></ActionIcon>
<ActionIcon variant="subtle"  color="gray"  aria-label="Bold"><Bold /></ActionIcon>
<ActionIcon variant="filled"  color="red"   aria-label="Delete"><Trash2 /></ActionIcon>

For destructive triggers, use variant="filled" color="red" — there is no separate destructive variant.

Sizes

Five sizes from xs (compact toolbar) to xl (hero CTA). Each is square, with the icon scaling via 1em against the size's font-size.

<ActionIcon size="xs" aria-label="Star"><Star /></ActionIcon>
<ActionIcon size="sm" aria-label="Star"><Star /></ActionIcon>
<ActionIcon size="md" aria-label="Star"><Star /></ActionIcon>
<ActionIcon size="lg" aria-label="Star"><Star /></ActionIcon>
<ActionIcon size="xl" aria-label="Star"><Star /></ActionIcon>

Radius

Independent radius scale, including full for circular triggers — perfect for FABs, like buttons, and avatar-adjacent controls.

<ActionIcon radius="sm" aria-label="Star"><Star /></ActionIcon>
<ActionIcon radius="md" aria-label="Star"><Star /></ActionIcon>
<ActionIcon radius="lg" aria-label="Star"><Star /></ActionIcon>
<ActionIcon radius="full" color="brand" aria-label="Add"><Plus /></ActionIcon>

Loading

The icon is swapped for an inline spinner, the element is marked aria-busy="true", and pointer events are suppressed. The accessible name still comes from aria-label — screen readers will announce both the label and the busy state.

import { Star } from 'lucide-react';

const [pending, startTransition] = useTransition();

<ActionIcon
  color="brand"
  isLoading={pending}
  aria-label="Star repository"
  onClick={() => startTransition(toggleStar)}
>
  <Star />
</ActionIcon>

Toolbar pattern

A row of subtle ActionIcons with radius="md" is the workhorse for editor toolbars and table row actions.

import { Bold, Code, Italic, Link2, Underline } from 'lucide-react';

<div role="toolbar" aria-label="Format">
  <ActionIcon variant="subtle" color="gray" size="sm" aria-label="Bold"><Bold /></ActionIcon>
  <ActionIcon variant="subtle" color="gray" size="sm" aria-label="Italic"><Italic /></ActionIcon>
  <ActionIcon variant="subtle" color="gray" size="sm" aria-label="Underline"><Underline /></ActionIcon>
</div>

Polymorphic with asChild

Same Ark UI polymorphism as Button. Wrap a link to keep client-side navigation while inheriting the icon-button styling.

import Link from 'next/link';
import { User } from 'lucide-react';

<ActionIcon asChild variant="subtle" color="gray" aria-label="Open profile">
  <Link href="/profile">
    <User />
  </Link>
</ActionIcon>

API

Prop

Type

All other props are forwarded to the underlying button (or asChild element).

When to use which

  • Reach for Button when the trigger has a visible text label, with or without supporting icons.
  • Reach for ActionIcon when the trigger is icon-only and the meaning lives in aria-label + tooltip.
  • For an icon plus a text label, use Button with leftSlot / rightSlot — not ActionIcon.

On this page