Toggle

Material Design 3 toggle button component for selecting between multiple options or toggling states

Toggle

Toggle buttons allow users to select one or more options from a set of choices. Unlike switches, toggle buttons are used for selecting between multiple options or toggling states in a group context.

Basic Usage

Basic Toggle

States

Default State

The default toggle button has an outlined appearance:

Default State

Active State

Use toggle-btn-active or active class to show the selected state:

Active State

Disabled State

Disabled toggles are non-interactive:

Disabled State

Loading State

Show a loading indicator for asynchronous operations:

Loading State

Variants

Color Variants

Toggle buttons support primary, secondary, and tertiary colors when active:

Color Variants

Outlined Variant

The default style with transparent background:

Outlined Variant

Filled Variant

Toggle with filled background:

Filled Variant

Chip Style

Rounded chip-like toggle buttons:

Chip Style

Segmented Control

iOS-style segmented control appearance:

Segmented Control

Sizes

Three sizes are available:

Toggle Sizes

Toggle with Icons

Icon and Text

Combine icons with text labels:

Icon and Text

Icon-Only Toggle

Compact icon-only toggles:

Icon-Only Toggle

Toggle Groups

Horizontal Group

Group multiple toggle buttons:

Horizontal Group

Vertical Group

Stack toggles vertically:

Vertical Group

Exclusive Selection

Radio-like behavior where only one option can be selected:

Exclusive Selection

Icon Group

Group of icon-only toggles:

Icon Group

Full Width

Single Toggle

Full Width Single Toggle

Full Width Group

Full Width Group

Badge Indicator

Toggle with a badge indicator (e.g., for notifications):

Badge Indicator

Common Patterns

Text Formatting Toolbar

Text Formatting Toolbar

View Switcher

View Switcher

Filter Chips

Filter Chips

Tab-like Navigation

Tab-like Navigation

Best Practices

Selection Behavior

  • Multi-select: Allow multiple toggles to be active simultaneously for independent options
  • Single-select: Use exclusive groups for mutually exclusive options
  • Toggle: Use for simple on/off states

Selection Behavior

Accessibility

  • Provide aria-label for icon-only toggles
  • Use aria-pressed="true" or aria-pressed="false" to indicate state
  • Ensure keyboard navigation works properly
  • Use sufficient color contrast

Accessibility Example

Visual Feedback

Always provide clear visual feedback for:

  • Hover state (automatic)
  • Active/selected state (use toggle-btn-active)
  • Focus state (automatic)
  • Disabled state (use disabled attribute)

Touch Targets

Ensure toggle buttons meet minimum touch target sizes (44×44px):

Touch Targets

Framework Examples

React

interface ToggleProps {
  active?: boolean;
  variant?: 'outlined' | 'filled' | 'chip';
  color?: 'primary' | 'secondary' | 'tertiary';
  size?: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  onChange?: (active: boolean) => void;
}

export function Toggle({
  active = false,
  variant = 'outlined',
  color = 'primary',
  size = 'md',
  children,
  onChange,
  ...props
}: ToggleProps) {
  return (
    <button
      className={`toggle-btn ${variant ? `toggle-${variant}` : ''} ${active ? 'toggle-btn-active' : ''} ${color && color !== 'primary' ? `toggle-btn-${color}` : ''} ${size !== 'md' ? `toggle-btn-${size}` : ''}`}
      onClick={() => onChange?.(!active)}
      aria-pressed={active}
      {...props}
    >
      {children}
    </button>
  );
}

// Toggle Group Component
interface ToggleGroupProps {
  value: string[];
  onChange: (value: string[]) => void;
  exclusive?: boolean;
  children: React.ReactNode;
}

export function ToggleGroup({
  value,
  onChange,
  exclusive = false,
  children
}: ToggleGroupProps) {
  return (
    <div className={`toggle-group ${exclusive ? 'toggle-group-exclusive' : ''}`}>
      {children}
    </div>
  );
}

// Usage
function App() {
  const [formatting, setFormatting] = React.useState(['bold']);
  const [alignment, setAlignment] = React.useState('left');

  return (
    <>
      {/* Multi-select */}
      <ToggleGroup value={formatting} onChange={setFormatting}>
        <Toggle active={formatting.includes('bold')}>Bold</Toggle>
        <Toggle active={formatting.includes('italic')}>Italic</Toggle>
        <Toggle active={formatting.includes('underline')}>Underline</Toggle>
      </ToggleGroup>

      {/* Exclusive selection */}
      <ToggleGroup value={[alignment]} onChange={v => setAlignment(v[0])} exclusive>
        <Toggle active={alignment === 'left'}>Left</Toggle>
        <Toggle active={alignment === 'center'}>Center</Toggle>
        <Toggle active={alignment === 'right'}>Right</Toggle>
      </ToggleGroup>
    </>
  );
}

Vue

<template>
  <button
    :class="classes"
    :aria-pressed="active"
    @click="$emit('change', !active)"
    v-bind="$attrs"
  >
    <slot />
  </button>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({
  active: {
    type: Boolean,
    default: false
  },
  variant: {
    type: String,
    default: 'outlined'
  },
  color: {
    type: String,
    default: 'primary'
  },
  size: {
    type: String,
    default: 'md'
  }
});

defineEmits(['change']);

const classes = computed(() => {
  const baseClass = 'toggle-btn';
  const variantClass = props.variant !== 'outlined' ? `toggle-${props.variant}` : '';
  const activeClass = props.active ? 'toggle-btn-active' : '';
  const colorClass = props.active && props.color !== 'primary' ? `toggle-btn-${props.color}` : '';
  const sizeClass = props.size !== 'md' ? `toggle-btn-${props.size}` : '';

  return [baseClass, variantClass, activeClass, colorClass, sizeClass].filter(Boolean).join(' ');
});
</script>

<!-- ToggleGroup.vue -->
<template>
  <div :class="groupClasses">
    <slot />
  </div>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({
  exclusive: {
    type: Boolean,
    default: false
  },
  vertical: {
    type: Boolean,
    default: false
  }
});

const groupClasses = computed(() => {
  return [
    'toggle-group',
    props.exclusive && 'toggle-group-exclusive',
    props.vertical && 'toggle-group-vertical'
  ].filter(Boolean).join(' ');
});
</script>

<!-- Usage -->
<template>
  <ToggleGroup :exclusive="true">
    <Toggle :active="alignment === 'left'" @change="alignment = 'left'">
      Left
    </Toggle>
    <Toggle :active="alignment === 'center'" @change="alignment = 'center'">
      Center
    </Toggle>
    <Toggle :active="alignment === 'right'" @change="alignment = 'right'">
      Right
    </Toggle>
  </ToggleGroup>
</template>

<script setup>
import { ref } from 'vue';
const alignment = ref('left');
</script>

JavaScript Interaction

Basic Toggle State

// Single toggle
const toggle = document.querySelector('.toggle-btn');
toggle.addEventListener('click', function() {
  this.classList.toggle('toggle-btn-active');
  const isActive = this.classList.contains('toggle-btn-active');
  this.setAttribute('aria-pressed', isActive);
});

// Toggle group with exclusive selection
const group = document.querySelector('.toggle-group-exclusive');
const buttons = group.querySelectorAll('.toggle-btn');

buttons.forEach(button => {
  button.addEventListener('click', function() {
    // Remove active from all buttons
    buttons.forEach(btn => {
      btn.classList.remove('toggle-btn-active');
      btn.setAttribute('aria-pressed', 'false');
    });

    // Add active to clicked button
    this.classList.add('toggle-btn-active');
    this.setAttribute('aria-pressed', 'true');
  });
});

// Multi-select toggle group
const multiGroup = document.querySelector('.toggle-group:not(.toggle-group-exclusive)');
const multiButtons = multiGroup.querySelectorAll('.toggle-btn');

multiButtons.forEach(button => {
  button.addEventListener('click', function() {
    const isActive = this.classList.toggle('toggle-btn-active');
    this.setAttribute('aria-pressed', isActive);
  });
});

API Reference

Class Names

ClassDescription
.toggle-btnBase toggle button styles (required)
.toggle-btn-activeActive/selected state
.toggle-btn-secondarySecondary color variant (when active)
.toggle-btn-tertiaryTertiary color variant (when active)
.toggle-btn-smSmall size
.toggle-btn-lgLarge size
.toggle-btn-icon-onlyIcon-only toggle
.toggle-outlinedOutlined variant (default)
.toggle-filledFilled variant
.toggle-chipChip-style variant
.toggle-segmentedSegmented control container
.toggle-badgeToggle with badge indicator
.toggle-fullFull-width toggle
.toggle-loadingLoading state
.toggle-groupToggle group container
.toggle-group-verticalVertical toggle group
.toggle-group-exclusiveExclusive selection group
.toggle-group-fullFull-width toggle group
.toggle-iconIcon element styling

States

StateImplementation
DefaultBase .toggle-btn class
HoverAutomatic hover styles
ActiveAdd .toggle-btn-active or .active class
FocusAutomatic focus-visible styles
DisabledAdd disabled attribute or .toggle-btn-disabled class
LoadingAdd .toggle-loading class

Combinations

You can combine classes for different effects:

Class Combinations

  • Button - Action buttons
  • Checkbox - Multi-select form inputs
  • Radio - Single-select form inputs
  • Switch - Binary on/off toggle

See Also