EXEPERTAI LAB

Research alpha

Time Machine
EXEPERTAI LAB
GALLERY / COLLECTION
← Browse Astryx gallery
Button·component·@astryxdesign/core

Toggle Button

A button that toggles between pressed and unpressed states. Thin wrapper over Button with controlled toggle pattern, icon swap, and font weight emphasis.

Open in Playground @astryxdesign/core/ToggleButton

Showcases and examples

5 documented examples

Toggle Button

ToggleButton API entry

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

PropType and behavior
labelstring · required
Accessible label for the button. Used as visible text, or as aria-label for icon-only buttons.
isPressedboolean
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.
isDisabledboolean · default false
Whether the button is disabled.
isLoadingboolean · default false
Whether the button shows a loading spinner.
iconReactNode
Icon element. When provided without children, button becomes icon-only with tooltip from label.
Slot: Icon
isIconOnlyboolean · default false
When true, renders as a square icon-only button with label as the aria-label and an automatic tooltip from the label.
pressedIconReactNode
Icon shown when pressed. Falls back to icon if not provided.
Slot: Icon
childrenReactNode
Visible content. If omitted with icon, button becomes icon-only.
tooltipstring
Tooltip text shown on hover.
valuestring
Value identifier when used inside ToggleButtonGroup. Required in groups.
data-testidstring
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.

States: Disabled

Theming

Targets

astryx-toggle-button-group
astryx-toggle-button

Visual props: elevation

States: isPressed