Icon Buttons
Icon buttons let people take action with a single icon.
Icon buttons let people take action with a single icon. They are compact, high-density components used for the most frequent actions — like liking, bookmarking, or sharing. They morph in shape and color to communicate context and state.
Introduction
The MD3 Expressive Icon Button is a refined version of the standard button, optimized for density and clarity. It supports four primary color styles (Standard, Filled, Tonal, and Outlined) and can function as a simple action or a stateful toggle. In the Expressive update, icon buttons feature smooth shape-morphing transitions that provide tactile confirmation of interaction.
Anatomy
- Container: The surface of the button. Supports multiple shapes (round, square) and variants.
- Icon: The visual indicator of the action. Should be universally understood.
- State Layer: Handles hover and pressed states with a ripple effect and morphing.
- Selection Indicator (Toggle): In toggle mode, the container color or icon fill changes to indicate the active state.
Variants
Color Styles
- Filled: High emphasis. Best for the primary action in a section.
- Tonal: Medium-high emphasis. A softer alternative to Filled.
- Outlined: Medium emphasis. Uses a border for visual separation.
- Standard: Low emphasis. Ideal for toolbars and overflow menus.
Toggle Mode
Icon buttons can toggle between two states. When selected, the button can morph its shape (e.g., from round to rounded-square) and its color to provide clear feedback.
Selected Icon
Instead of conditional rendering in children, use the selectedIcon prop to render an active icon variant (such as filled) automatically when selected={true}.
Features
Shapes
Choose between Round (classic) and Square (modern/expressive) base shapes. Both support morphing on interaction.
Sizing
Supports five sizes (XS to XL), allowing you to match the density of your interface.
Width Variants
In MD3 Expressive, icon buttons support three container width variants (default 1:1, narrow, wide) to fit different interface layouts and spatial contexts.
Loading State
Displays a loading indicator while a process is in progress, automatically disabling interaction and updating ARIA states.
Usage
Basic Action
import { IconButton, Icon } from "@bug-on/m3-expressive";
<IconButton
icon={<Icon name="share" />}
aria-label="Share content"
onClick={() => handleShare()}
/>
Toggle Button
const [liked, setLiked] = useState(false);
<IconButton
variant="toggle"
selected={liked}
onClick={() => setLiked(!liked)}
icon={<Icon name="favorite" />}
selectedIcon={<Icon name="favorite" fill={1} />}
aria-label="Like"
colorStyle="tonal"
/>
Width Variants
<IconButton
icon={<Icon name="tune" />}
aria-label="Filters"
width="wide"
/>
Next.js Integration (asChild)
Use the asChild prop to render a different element (like a Next.js Link) while maintaining IconButton styling and ripple effects.
import Link from "next/link";
import { IconButton, Icon } from "@bug-on/m3-expressive";
<IconButton asChild aria-label="Settings">
<Link href="/settings">
<Icon name="settings" />
</Link>
</IconButton>
Best Practices
Do
- Always provide a descriptive
aria-labelsince the button has no text. - Use tooltips to explain the action if the icon is not universally known.
- Use the Standard variant for low-priority actions in a toolbar.
- Ensure the icon is centered perfectly within the container.
Don't
- Don't use more than one Filled icon button in a single area.
- Avoid using very small sizes (XS) for primary actions on touch devices.
- Don't use complex or ambiguous icons; stick to well-known symbols.
Accessibility
- Required Label:
aria-labelmust be provided for every icon button. - Toggle State: Uses
aria-pressedfor toggle variants. - Touch Target: Even smaller visual sizes maintain a minimum 48x48dp touch target.
- Motion: Morphing animations respect
prefers-reduced-motion.
API Reference
IconButton
| Prop | Type | Default | Description |
|---|---|---|---|
colorStyle | "standard" | "filled" | "tonal" | "outlined" | "standard" | Visual variant. |
variant | "action" | "toggle" | "action" | Behavior mode. |
selected | boolean | false | Required for toggle variant. |
selectedIcon | ReactNode | — | Active icon to display when variant="toggle" and selected={true}. |
icon | ReactNode | — | The icon to display. |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Physical container dimensions (height/diameter). |
width | "narrow" | "default" | "wide" | "default" | Container slot width ratio per MD3 Expressive specification (MD3SlotWidth). |
shape | "round" | "square" | "round" | Base container shape. |
loading | boolean | false | Shows loading indicator. |
asChild | boolean | false | Renders as the child element while keeping styles. |
className | string | — | Custom classes. |