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.
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.
| Property | Default | Description |
|---|---|---|
| --toggle-group-bg | var(--color-muted) | Controls group background color. |
| --toggle-group-border-color | var(--color-border) | Controls group border color. |
| --toggle-group-border-width | var(--border-width-sm) | Controls group border width. |
| --toggle-group-color | var(--color-foreground) | Controls group text color. |
| --toggle-group-gap | var(--border-width-sm) | Controls spacing between items. |
| --toggle-group-ghost-bg | transparent | Controls ghost variant group background. |
| --toggle-group-ghost-border-color | transparent | Controls ghost variant group border color. |
| --toggle-group-ghost-padding | 0 | Controls ghost variant group padding. |
| --toggle-group-padding | 0.125rem | Controls group inner padding. |
| --toggle-group-item-radius | var(--radius-md) | Controls item corner radius. |
| --toggle-group-outline-bg | var(--color-background) | Controls outline variant group background. |
| --toggle-group-radius | var(--radius-lg) | Controls group corner radius. |
Interactive variables scoped for docs preview without changing size scale tokens.
| Property | Value | Default | Description |
|---|---|---|---|
| --toggle-group-bg | var(--color-muted) | Controls group background color. | |
| --toggle-group-border-color | var(--color-border) | Controls group border color. | |
| --toggle-group-color | var(--color-foreground) | Controls group text color. | |
| --toggle-group-item-radius | var(--radius-md) | Controls item corner radius. | |
| --toggle-group-radius | var(--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>| Part | Role |
|---|---|
ToggleGroup | Root state machine. Controls selected values, arrow-key navigation, and orientation. |
ToggleGroupItem | Interactive 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.
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" },];