moduix

ToggleGroup

A set of related toggle buttons that share selection state.

API Reference

Original primitive API

Behavior, accessibility details, and low-level props are documented by Base UI.

Base UI API

Basic

import { ToggleGroup, ToggleGroupItem } from "moduix";export function BasicToggleGroupDemo() {  return (    <ToggleGroup defaultValue={["left"]} aria-label="Text alignment">      {items.map((item) => (        <ToggleGroupItem key={item.value} value={item.value}>          {item.label}        </ToggleGroupItem>      ))}    </ToggleGroup>  );}
const items = [  { value: "left", label: "Left" },  { value: "center", label: "Center" },  { value: "right", label: "Right" },];

Full list of component variables available for project-level overrides.

PropertyDefaultDescription
--toggle-group-bgvar(--color-muted)Controls group background color.
--toggle-group-border-colorvar(--color-border)Controls group border color.
--toggle-group-border-widthvar(--border-width-sm)Controls group border width.
--toggle-group-colorvar(--color-foreground)Controls group text color.
--toggle-group-gapvar(--border-width-sm)Controls spacing between items.
--toggle-group-ghost-bgtransparentControls ghost variant group background.
--toggle-group-ghost-border-colortransparentControls ghost variant group border color.
--toggle-group-ghost-padding0Controls ghost variant group padding.
--toggle-group-padding0.125remControls group inner padding.
--toggle-group-item-radiusvar(--radius-md)Controls item corner radius.
--toggle-group-outline-bgvar(--color-background)Controls outline variant group background.
--toggle-group-radiusvar(--radius-lg)Controls group corner radius.

Interactive variables scoped for docs preview without changing size scale tokens.

PropertyValueDefaultDescription
--toggle-group-bgvar(--color-muted)Controls group background color.
--toggle-group-border-colorvar(--color-border)Controls group border color.
--toggle-group-colorvar(--color-foreground)Controls group text color.
--toggle-group-item-radiusvar(--radius-md)Controls item corner radius.
--toggle-group-radiusvar(--radius-lg)Controls group corner radius.

Anatomy

ToggleGroup is a single root that coordinates a set of ToggleGroupItem buttons. Keep every choice inside the same group so keyboard navigation, pressed state, and selection rules (multiple or single-select) stay consistent.

ToggleGroup
└─ ToggleGroupItem[value]
   └─ label or icon content
<ToggleGroup defaultValue={['left']} aria-label="Text alignment">
  <ToggleGroupItem value="left">Left</ToggleGroupItem>
  <ToggleGroupItem value="center">Center</ToggleGroupItem>
  <ToggleGroupItem value="right">Right</ToggleGroupItem>
</ToggleGroup>
PartRole
ToggleGroupRoot state machine. Controls selected values, arrow-key navigation, and orientation.
ToggleGroupItemInteractive toggle button with a unique value used by defaultValue, value, and onValueChange.

ToggleGroup does not include service layers such as portal, backdrop, viewport, or hidden internal slots. In most cases, keep structure and behavior defaults and style the visible parts with className on the group and on each ToggleGroupItem.

ToggleGroupItem renders Toggle, so item-level styling also supports the Toggle CSS variables (--toggle-*). Use --toggle-group-* for the group container and --toggle-* for item states.

.filterGroup {
  --toggle-group-bg: transparent;
  --toggle-group-border-color: transparent;
  --toggle-group-padding: 0;
}

.filterItem {
  --toggle-ghost-bg-hover: var(--color-accent);
  --toggle-ghost-bg-pressed: var(--color-primary);
  --toggle-ghost-color-pressed: var(--color-primary-foreground);
}

Composition

Use ToggleGroup for root state and behavior props such as value, defaultValue, onValueChange, multiple, orientation, disabled, and loopFocus.

Use group-level props (variant, size, className) when all items should share one setup. Override a specific ToggleGroupItem with its own variant, size, or className when one option needs a different presentation.

Examples

Multiple

Use multiple when more than one item can be pressed at the same time.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function MultipleToggleGroupDemo() {  return (    <ToggleGroup      multiple      defaultValue={["bold", "italic"]}      aria-label="Text formatting"      size="icon-md"    >      <ToggleGroupItem value="bold" aria-label="Bold">        <strong>B</strong>      </ToggleGroupItem>      <ToggleGroupItem value="italic" aria-label="Italic">        <em>I</em>      </ToggleGroupItem>      <ToggleGroupItem value="underline" aria-label="Underline">        <span className={styles.underline}>U</span>      </ToggleGroupItem>    </ToggleGroup>  );}
.underline {  text-decoration: underline;}

Variants

Use variant on the group to apply the same visual treatment to every item.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function ToggleGroupVariantsDemo() {  return (    <div className={styles.stack}>      {variants.map((variant) => (        <ToggleGroup          key={variant.value}          defaultValue={["one"]}          aria-label={variant.label}          variant={variant.value}        >          {items.map((item) => (            <ToggleGroupItem key={item.value} value={item.value}>              {item.label}            </ToggleGroupItem>          ))}        </ToggleGroup>      ))}    </div>  );}
.stack {  display: flex;  flex-direction: column;  align-items: flex-start;  gap: var(--spacing-3);}
const variants = [  { value: "default", label: "Default variant" },  { value: "outline", label: "Outline variant" },  { value: "ghost", label: "Ghost variant" },] as const;const items = [  { value: "one", label: "One" },  { value: "two", label: "Two" },  { value: "three", label: "Three" },];

Sizes

Use text sizes for labeled groups and icon sizes for compact toolbar controls.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function ToggleGroupSizesDemo() {  return (    <div className={styles.stack}>      {sizes.map((group) => (        <ToggleGroup          key={group.size}          defaultValue={[group.items[0].value]}          aria-label={group.label}          size={group.size}        >          {group.items.map((item) => (            <ToggleGroupItem key={item.value} value={item.value}>              {item.label}            </ToggleGroupItem>          ))}        </ToggleGroup>      ))}    </div>  );}
.stack {  display: flex;  flex-direction: column;  align-items: flex-start;  gap: var(--spacing-3);}
const sizes = [  {    size: "xs",    label: "Extra-small size",    items: [      { value: "xs", label: "XS" },      { value: "sm", label: "SM" },    ],  },  {    size: "sm",    label: "Small size",    items: [      { value: "sm", label: "Small" },      { value: "md", label: "Medium" },    ],  },  {    size: "md",    label: "Medium size",    items: [      { value: "md", label: "Medium" },      { value: "lg", label: "Large" },    ],  },  {    size: "lg",    label: "Large size",    items: [      { value: "lg", label: "Large" },      { value: "xl", label: "Extra" },    ],  },] as const;

Vertical

Set orientation="vertical" when the choices should stack in a column.

import { ToggleGroup, ToggleGroupItem } from "moduix";export function VerticalToggleGroupDemo() {  return (    <ToggleGroup      defaultValue={["list"]}      orientation="vertical"      aria-label="View mode"      variant="outline"    >      {items.map((item) => (        <ToggleGroupItem key={item.value} value={item.value}>          {item.label}        </ToggleGroupItem>      ))}    </ToggleGroup>  );}
const items = [  { value: "list", label: "List" },  { value: "grid", label: "Grid" },  { value: "map", label: "Map" },];

Disabled

Use disabled on the group or on a specific item to prevent interaction.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function DisabledToggleGroupDemo() {  return (    <div className={styles.row}>      <ToggleGroup defaultValue={["one"]} aria-label="Disabled group" disabled>        {items.map((item) => (          <ToggleGroupItem key={item.value} value={item.value}>            {item.label}          </ToggleGroupItem>        ))}      </ToggleGroup>      <ToggleGroup defaultValue={["one"]} aria-label="Disabled item">        {items.map((item) => (          <ToggleGroupItem key={item.value} value={item.value} disabled={item.disabled}>            {item.label}          </ToggleGroupItem>        ))}      </ToggleGroup>    </div>  );}
.row {  display: flex;  flex-wrap: wrap;  align-items: center;  justify-content: center;  gap: var(--spacing-3);}
const items = [  { value: "one", label: "One" },  { value: "two", label: "Two", disabled: true },];

Loop Focus

Set loopFocus={false} when arrow-key navigation should stop at the first or last item.

import { ToggleGroup, ToggleGroupItem } from "moduix";export function LoopFocusToggleGroupDemo() {  return (    <ToggleGroup defaultValue={["day"]} aria-label="Schedule range" loopFocus={false}>      {items.map((item) => (        <ToggleGroupItem key={item.value} value={item.value}>          {item.label}        </ToggleGroupItem>      ))}    </ToggleGroup>  );}
const items = [  { value: "day", label: "Day" },  { value: "week", label: "Week" },  { value: "month", label: "Month" },];

Controlled

Control value from React state when the group needs to coordinate with other UI.

Current value: favorites
import { BellIcon, CheckSmallIcon, StarIcon, ToggleGroup, ToggleGroupItem } from "moduix";import { useState } from "react";import styles from "./toggle-group.module.css";export function ControlledToggleGroupDemo() {  const [value, setValue] = useState(["favorites"] as string[]);  return (    <div className={styles.stack}>      <ToggleGroup        value={value}        onValueChange={setValue}        aria-label="Controlled options"        multiple      >        <ToggleGroupItem value="favorites">          {value.includes("favorites") ? <CheckSmallIcon /> : <StarIcon />}          Favorites        </ToggleGroupItem>        <ToggleGroupItem value="alerts">          <BellIcon />          Alerts        </ToggleGroupItem>      </ToggleGroup>      <span className={styles.hint}>Current value: {value.join(", ") || "empty"}</span>    </div>  );}
.stack {  display: flex;  flex-direction: column;  align-items: flex-start;  gap: var(--spacing-3);}.hint {  color: var(--color-muted-foreground);  font-size: var(--text-xs);  line-height: var(--line-height-text-xs);}

Icons

Pass icons as children. Icon-only items must include an accessible label.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function IconToggleGroupDemo() {  return (    <ToggleGroup defaultValue={["left"]} aria-label="Text alignment" size="icon-md">      <ToggleGroupItem value="left" aria-label="Align left">        <svg className={styles.customIcon} viewBox="0 0 16 16" fill="none" aria-hidden="true">          <path            d="M2.5 3.5h11M2.5 8h8M2.5 12.5h11"            stroke="currentColor"            strokeWidth="1.5"            strokeLinecap="round"          />        </svg>      </ToggleGroupItem>      <ToggleGroupItem value="center" aria-label="Align center">        <svg className={styles.customIcon} viewBox="0 0 16 16" fill="none" aria-hidden="true">          <path            d="M2.5 3.5h11M4 8h8M2.5 12.5h11"            stroke="currentColor"            strokeWidth="1.5"            strokeLinecap="round"          />        </svg>      </ToggleGroupItem>      <ToggleGroupItem value="right" aria-label="Align right">        <svg className={styles.customIcon} viewBox="0 0 16 16" fill="none" aria-hidden="true">          <path            d="M2.5 3.5h11M5.5 8h8M2.5 12.5h11"            stroke="currentColor"            strokeWidth="1.5"            strokeLinecap="round"          />        </svg>      </ToggleGroupItem>    </ToggleGroup>  );}
.customIcon {  width: 1rem;  height: 1rem;}

Custom Styles

Pass className to the group and items when styling with CSS Modules, Tailwind CSS, or CSS-in-JS. Use --toggle-group-* to style the group container and --toggle-* to style ToggleGroupItem states such as hover and pressed.

import { ToggleGroup, ToggleGroupItem } from "moduix";import styles from "./toggle-group.module.css";export function CustomStylesToggleGroupDemo() {  return (    <ToggleGroup      defaultValue={["day"]}      aria-label="Schedule density"      className={styles.customGroup}    >      {items.map((item) => (        <ToggleGroupItem key={item.value} value={item.value} className={styles.customItem}>          {item.label}        </ToggleGroupItem>      ))}    </ToggleGroup>  );}
.customGroup {  --toggle-group-bg: color-mix(in oklab, var(--color-primary) 8%, transparent);  --toggle-group-border-color: color-mix(in oklab, var(--color-primary) 38%, var(--color-border));  --toggle-group-gap: var(--spacing-1);  --toggle-group-padding: var(--spacing-1);  --toggle-group-radius: var(--radius-md);  --toggle-default-bg-pressed: var(--color-primary);  --toggle-default-border-color-pressed: var(--color-primary);  --toggle-default-color-pressed: var(--color-primary-foreground);}.customItem {  --toggle-group-item-radius: var(--radius-sm);  --toggle-padding-x-md: var(--spacing-3);}
const items = [  { value: "day", label: "Day" },  { value: "week", label: "Week" },  { value: "month", label: "Month" },];

On this page