A button that toggles between pressed and unpressed states. Thin wrapper over Button with controlled toggle pattern, icon swap, and font weight emphasis.
Preview loads on approachPreview loads on approach
Exact source · toggle-button-showcase
// Copyright (c) Meta Platforms, Inc. and affiliates.
'use client';
import {useState} from 'react';
import {ToggleButton} from '@astryxdesign/core/ToggleButton';
import {Stack} from '@astryxdesign/core/Layout';
import {Icon} from '@astryxdesign/core/Icon';
import {
StarIcon as StarOutline,
BookmarkIcon as BookmarkOutline,
BellIcon,
BellSlashIcon,
} from '@heroicons/react/24/outline';
import {
StarIcon as StarSolid,
BookmarkIcon as BookmarkSolid,
} from '@heroicons/react/24/solid';
export default function ToggleButtonShowcase() {
const [isFavorited, setIsFavorited] = useState(false);
const [isBookmarked, setIsBookmarked] = useState(true);
const [isMuted, setIsMuted] = useState(false);
return (
<Stack direction="horizontal" gap={3} vAlign="center">
<ToggleButton
label="Favorite"
icon={<Icon icon={StarOutline} />}
pressedIcon={<Icon icon={StarSolid} />}
isPressed={isFavorited}
onPressedChange={setIsFavorited}
isIconOnly
/>
<ToggleButton
label="Bookmark"
icon={<Icon icon={BookmarkOutline} />}
pressedIcon={<Icon icon={BookmarkSolid} />}
isPressed={isBookmarked}
onPressedChange={setIsBookmarked}
isIconOnly
/>
<ToggleButton
label="Notifications"
icon={<Icon icon={BellIcon} />}
pressedIcon={<Icon icon={BellSlashIcon} />}
isPressed={isMuted}
onPressedChange={setIsMuted}>
Notifications
</ToggleButton>
</Stack>
);
}
Usage
ToggleButton switches between selected and unselected states to represent a persistent on/off choice. Use it standalone for binary actions like bold, mute, or favorite, or inside a ToggleButtonGroup for single-select or multi-select toolbar controls.
Use a filled or colored icon for the pressed state so users can see the current state at a glance: an outline star vs a solid star, for example.
Keep the label identical between pressed and unpressed states. Let the visual treatment (icon, weight, background) communicate the change.
Wrap related toggles in a ToggleButtonGroup with an accessible label so screen readers announce them as a connected set.
Don't use a ToggleButton for one-time actions like "Submit" or "Delete"; those are regular Buttons, not toggles.
Don't mix ToggleButtons with regular Buttons inside the same group; use only ToggleButtons in a ToggleButtonGroup.
Don't use a ToggleButton for on/off settings that persist across sessions; use a Switch instead, which better communicates "setting" semantics.
Typed props
Prop
Type and behavior
label
string · required
Accessible label for the button. Used as visible text, or as aria-label for icon-only buttons.
isPressed
boolean
Whether the button is currently pressed. Ignored for a ToggleButtonGroup member with a value.
onPressedChange
(isPressed: boolean, event: MouseEvent) => void
Called synchronously when pressed state should change. Receives the next state and the click event; call event.preventDefault() to skip pressedChangeAction. Without an Action, the callback produces no Action-pending feedback. Ignored for a ToggleButtonGroup member with a value.
pressedChangeAction
(isPressed: boolean) => void | Promise<void>
Action handler for API- or navigation-backed toggles, run in a transition after the synchronous onPressedChange callback unless that callback calls event.preventDefault(). Works without onPressedChange. Shows an optimistic pressed state and a spinner while pending, and remains interruptible by re-clicks. Omit it for callback-only toggles with no Action-pending feedback. Ignored for a ToggleButtonGroup member with a value.
size
'sm' | 'md' | 'lg' · default 'md'
Button size. Defaults to group size when inside a group.
elevation
'none' | 'low' | 'med' | 'high' · default 'none'
Resting shadow depth for floating (FAB-style) toggle buttons, mirroring Button. none is the default flat button; low/med/high map to the shadow token scale. Applies inside a ToggleButtonGroup as well — grouped children retain their own elevation.
isDisabled
boolean · default false
Whether the button is disabled.
isLoading
boolean · default false
Whether the button shows a loading spinner.
icon
ReactNode
Icon element. When provided without children, button becomes icon-only with tooltip from label.
Value identifier when used inside ToggleButtonGroup. Required in groups.
data-testid
string
Test selector for automated testing frameworks.
Anatomy
Icon · optional
A leading icon that represents the toggle action, like a star for favorite or bold "B" for formatting.
Pressed icon · optional
An alternate icon shown when pressed: typically a filled version of the default icon to reinforce the active state.
Label · required
The visible text or accessible name. For icon-only toggles, used as the aria-label and auto-tooltip.
Spinner · optional
Replaces the icon during async operations triggered by pressedChangeAction.
Accessibility
Text label
Color contrast · 1.4.3 Contrast (Minimum) · 4.5:1
The label must have at least 4.5:1 contrast with the button background when selected and unselected. For Hover and Pointer down, measure the final background after the overlay is applied.
States: Unselected, Selected, Hover, Pointer down
Essential icon or spinner arc
Color contrast · 1.4.11 Non-text Contrast · 3:1
An icon-only ToggleButton must have at least 3:1 contrast between its icon and button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check.
States: Icon only, Loading
Selected state indicator
Color contrast · 1.4.11 Non-text Contrast · 3:1 if relied upon
The selected background must reach 3:1 only when users need it to tell selected from unselected. Label weight or a changed icon can also show selection.
States: Selected
Visible control boundary
Color contrast · 1.4.11 Non-text Contrast · 3:1 if needed
The button edge needs 3:1 contrast only when users need it to see the control. A visible label or icon can show the control instead.
States: Rest
Keyboard focus indicator
Color contrast · 1.4.11 Non-text Contrast · 3:1
The focus outline must have at least 3:1 contrast with the area around the button. Check both selected and unselected states.
States: Focus visible
Disabled appearance
Color contrast · 1.4.3 and 1.4.11 exceptions · Not required
Disabled controls do not need to meet these contrast ratios.