# Checkbox Group (/docs/checkbox-group)





## API Reference [#api-reference]

<BaseUIReference href="https://base-ui.com/react/components/checkbox-group" />

## Basic [#basic]

<Preview cssProperties="checkboxGroupPlaygroundCssProperties">
  <CheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId } from "react";

          export function CheckboxGroupDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup aria-labelledby={labelId} defaultValue={["email"]}>
                <CheckboxGroupLabel id={labelId}>Notification Channels</CheckboxGroupLabel>
                <CheckboxGroupList>
                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value}>
                      <CheckboxGroupItemControl value={option.value} name="notifications" />
                      <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>

  <Preview.CSSProperties>
    {(context) => <CheckboxGroupCssPropertiesPanel {...context} />}
  </Preview.CSSProperties>

  <Preview.CSSPlayground>
    {(context) => <CheckboxGroupCssPlaygroundPanel {...context} />}
  </Preview.CSSPlayground>
</Preview>

## Anatomy [#anatomy]

Checkbox Group is composed as one shared state root with optional label and list wrappers.
Each row combines an interactive control and a text label; keep `CheckboxGroupItemControl`
and `CheckboxGroupItemLabel` in the same `CheckboxGroupItem` so selection state, focus,
and labeling stay aligned.

```text
CheckboxGroup
├─ CheckboxGroupLabel
└─ CheckboxGroupList
   └─ CheckboxGroupItem
      ├─ CheckboxGroupItemControl
      │  └─ CheckboxIndicator
      │     └─ CheckboxIndicatorIcon
      └─ CheckboxGroupItemLabel
```

```tsx
<CheckboxGroup aria-labelledby={labelId} defaultValue={['email']}>
  <CheckboxGroupLabel id={labelId}>Notification Channels</CheckboxGroupLabel>
  <CheckboxGroupList>
    <CheckboxGroupItem>
      <CheckboxGroupItemControl value="email" name="notifications" />
      <CheckboxGroupItemLabel>Email updates</CheckboxGroupItemLabel>
    </CheckboxGroupItem>
  </CheckboxGroupList>
</CheckboxGroup>
```

| Part                       | Role                                                                                        |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `CheckboxGroup`            | Root state machine. Manages selected values, `disabled`, `allValues`, and change callbacks. |
| `CheckboxGroupLabel`       | Optional group heading slot. Provides visible context for the entire set of options.        |
| `CheckboxGroupList`        | Optional layout slot that contains checkbox rows and supports group-level spacing/styling.  |
| `CheckboxGroupItem`        | Optional row wrapper that keeps one control and one text label grouped as a single option.  |
| `CheckboxGroupItemControl` | Interactive checkbox control for an item. Supports checkbox props and state-based styling.  |
| `CheckboxGroupItemLabel`   | Optional text label slot associated with the item control.                                  |

`CheckboxGroup` does not use service slots such as `portal`, `backdrop`, or `viewport`.
In most cases, keep behavior defaults and customize visible parts with `className`,
`classNames`, and CSS variables.

## Composition [#composition]

Use `CheckboxGroup` for shared Base UI state and behavior props such as `value`,
`defaultValue`, `onValueChange`, `allValues`, `disabled`, `render`, `className`, and
state-aware `style`. Use `allValues` together with a child checkbox `parent` prop for
select-all behavior.

`CheckboxGroupLabel`, `CheckboxGroupList`, `CheckboxGroupItem`, and
`CheckboxGroupItemLabel` are layout slots. Pass `className` to style each visible slot.
`CheckboxGroupItemControl` composes `Checkbox`, so it supports checkbox props such as `size`,
`nativeButton`, `render`, `name`, `value`, `checkedIcon`, `indeterminateIcon`, `classNames`,
and `slotProps`.

Use `className` for the checkbox root. Use `classNames` for the automatically rendered
indicator slots hidden behind the default checkbox composition. Use `slotProps.indicator`
for Base UI indicator props such as `keepMounted` without composing the indicator manually:

```tsx
<CheckboxGroupItemControl
  className={styles.customControl}
  classNames={{
    indicator: styles.customIndicator,
    indicatorIcon: styles.customIndicatorIcon,
    checkedIcon: styles.customCheckedIcon,
    indeterminateIcon: styles.customIndeterminateIcon,
  }}
  slotProps={{
    indicator: { keepMounted: true },
  }}
/>
```

## Examples [#examples]

### Sizes [#sizes]

Use `size` on `CheckboxGroupItemControl` to align the controls with different form densities.

<Preview>
  <CheckboxGroupSizesExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId } from "react";

          export function CheckboxGroupSizesDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup aria-labelledby={labelId} defaultValue={["md"]}>
                <CheckboxGroupLabel id={labelId}>Control Size</CheckboxGroupLabel>
                <CheckboxGroupList>
                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value}>
                      <CheckboxGroupItemControl value={option.value} size={option.value} />
                      <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "xs", label: "Extra-small" },
            { value: "sm", label: "Small" },
            { value: "md", label: "Medium" },
            { value: "lg", label: "Large" },
            { value: "xl", label: "Extra-large" },
          ] as const;
        `}
  </Preview.Data>
</Preview>

### Controlled [#controlled]

Control `value` from React state when the selected options need to coordinate with other UI.

<Preview>
  <ControlledCheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId, useState } from "react";

          export function ControlledCheckboxGroupDemo() {
            const labelId = useId();
            const [value, setValue] = useState(["push"] as string[]);

            return (
              <div className={styles.wrapper}>
                <CheckboxGroup aria-labelledby={labelId} value={value} onValueChange={setValue}>
                  <CheckboxGroupLabel id={labelId}>Active Alerts</CheckboxGroupLabel>
                  <CheckboxGroupList>
                    {options.map((option) => (
                      <CheckboxGroupItem key={option.value}>
                        <CheckboxGroupItemControl value={option.value} name="alerts" />
                        <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                      </CheckboxGroupItem>
                    ))}
                  </CheckboxGroupList>
                </CheckboxGroup>
                <span className={styles.hint}>Current value: {value.join(", ") || "none"}</span>
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .wrapper {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            gap: var(--spacing-2);
          }

          .hint {
            color: var(--color-muted-foreground);
            font-size: var(--text-xs);
            line-height: var(--line-height-text-xs);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>

### Disabled [#disabled]

Use `disabled` on the group to prevent interaction with every checkbox in the group.

<Preview>
  <DisabledCheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId } from "react";

          export function DisabledCheckboxGroupDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup aria-labelledby={labelId} defaultValue={["push"]} disabled>
                <CheckboxGroupLabel id={labelId}>Disabled Settings</CheckboxGroupLabel>
                <CheckboxGroupList>
                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value}>
                      <CheckboxGroupItemControl value={option.value} name="disabled-settings" />
                      <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>

### Parent Checkbox [#parent-checkbox]

Use `allValues` with a `parent` checkbox to create a select-all control that enters the
indeterminate state when only some child options are checked.

<Preview>
  <ParentCheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId, useState } from "react";

          export function ParentCheckboxGroupDemo() {
            const labelId = useId();
            const [value, setValue] = useState([] as string[]);

            return (
              <CheckboxGroup
                aria-labelledby={labelId}
                value={value}
                onValueChange={setValue}
                allValues={fruitValues}
              >
                <CheckboxGroupLabel id={labelId}>Fruits</CheckboxGroupLabel>
                <CheckboxGroupList>
                  <CheckboxGroupItem>
                    <CheckboxGroupItemControl parent />
                    <CheckboxGroupItemLabel>Select all</CheckboxGroupItemLabel>
                  </CheckboxGroupItem>

                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value}>
                      <CheckboxGroupItemControl value={option.value} />
                      <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "apple", label: "Apple" },
            { value: "orange", label: "Orange" },
            { value: "pear", label: "Pear" },
          ];

          const fruitValues = options.map((option) => option.value);
        `}
  </Preview.Data>
</Preview>

### Custom Icon [#custom-icon]

Pass `checkedIcon`, `indeterminateIcon`, `indicator`, or custom children to
`CheckboxGroupItemControl` to use any icon from your application or icon library.

<Preview>
  <CustomIconCheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId } from "react";

          function CustomPlusIcon() {
            return (
              <svg viewBox="0 0 10 10" fill="none" aria-hidden="true" focusable="false">
                <path
                  d="M5 1.5V8.5M1.5 5H8.5"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                />
              </svg>
            );
          }

          export function CustomIconCheckboxGroupDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup aria-labelledby={labelId} defaultValue={["email"]}>
                <CheckboxGroupLabel id={labelId}>Custom Indicators</CheckboxGroupLabel>
                <CheckboxGroupList>
                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value}>
                      <CheckboxGroupItemControl
                        value={option.value}
                        name="custom-indicators"
                        checkedIcon={<CustomPlusIcon />}
                      />
                      <CheckboxGroupItemLabel>{option.label}</CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>

### Custom Styles [#custom-styles]

Pass `className` to the group, label, list, item, control, and item label slots. Use
`classNames` on `CheckboxGroupItemControl` for internal checkbox slots such as `indicator`.

<Preview>
  <CustomStylesCheckboxGroupExample />

  <Preview.Code>
    {`
          import {
            CheckboxGroup,
            CheckboxGroupItem,
            CheckboxGroupItemControl,
            CheckboxGroupItemLabel,
            CheckboxGroupLabel,
            CheckboxGroupList,
          } from "moduix";
          import { useId } from "react";

          export function CustomStylesCheckboxGroupDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup
                aria-labelledby={labelId}
                defaultValue={["email"]}
                className={styles.customGroup}
              >
                <CheckboxGroupLabel id={labelId} className={styles.customLabel}>
                  Styled Channels
                </CheckboxGroupLabel>
                <CheckboxGroupList className={styles.customList}>
                  {options.map((option) => (
                    <CheckboxGroupItem key={option.value} className={styles.customItem}>
                      <CheckboxGroupItemControl
                        value={option.value}
                        name="styled-notifications"
                        className={styles.customControl}
                        classNames={{ indicator: styles.customIndicator }}
                      />
                      <CheckboxGroupItemLabel className={styles.customItemLabel}>
                        {option.label}
                      </CheckboxGroupItemLabel>
                    </CheckboxGroupItem>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .customGroup,
          .customList,
          .customItem {
            gap: var(--spacing-3);
          }

          .customLabel,
          .customItemLabel {
            color: var(--color-primary);
          }

          .customControl {
            border-color: var(--color-primary);

            &[data-checked] {
              background-color: var(--color-primary);
            }
          }

          .customIndicator {
            color: var(--color-primary-foreground);
          }
        `}
  </Preview.CSS>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>

### Field Composition [#field-composition]

Use `CheckboxField`, `Checkbox`, and `CheckboxLabel` directly inside `CheckboxGroup` when you
want a compact enclosing-label composition.

<Preview>
  <CheckboxGroupFieldCompositionExample />

  <Preview.Code>
    {`
          import {
            Checkbox,
            CheckboxField,
            CheckboxGroup,
            CheckboxGroupLabel,
            CheckboxGroupList,
            CheckboxLabel,
          } from "moduix";
          import { useId } from "react";

          export function CheckboxGroupFieldCompositionDemo() {
            const labelId = useId();

            return (
              <CheckboxGroup aria-labelledby={labelId} defaultValue={["email"]}>
                <CheckboxGroupLabel id={labelId}>Channels</CheckboxGroupLabel>
                <CheckboxGroupList>
                  {options.map((option) => (
                    <CheckboxField key={option.value}>
                      <Checkbox value={option.value} name="field-composition" />
                      <CheckboxLabel>{option.label}</CheckboxLabel>
                    </CheckboxField>
                  ))}
                </CheckboxGroupList>
              </CheckboxGroup>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>

### Sibling Label [#sibling-label]

Use `nativeButton` with `render={<button />}` for the sibling `label` pattern with `htmlFor`
and `id`.

<Preview>
  <CheckboxGroupSiblingLabelNativeButtonExample />

  <Preview.Code>
    {`
          import { CheckboxGroup, CheckboxGroupItemControl } from "moduix";
          import { useId } from "react";

          export function SiblingLabelCheckboxGroupDemo() {
            const id = useId();
            const labelId = useId();

            return (
              <div className={styles.siblingRow}>
                <div id={labelId} className={styles.hint}>
                  Channels
                </div>
                <CheckboxGroup defaultValue={["email"]} aria-labelledby={labelId}>
                  <CheckboxGroupItemControl
                    nativeButton
                    render={<button />}
                    id={id}
                    value="email"
                    name="sibling-notifications"
                  />
                </CheckboxGroup>
                <label htmlFor={id} className={styles.label}>
                  Email updates
                </label>
              </div>
            );
          }
        `}
  </Preview.Code>

  <Preview.CSS>
    {`
          .siblingRow {
            display: flex;
            align-items: center;
            gap: var(--checkbox-gap, var(--spacing-2));
          }

          .hint {
            color: var(--color-muted-foreground);
            font-size: var(--text-xs);
            line-height: var(--line-height-text-xs);
          }

          .label {
            color: var(--checkbox-label-color, var(--color-foreground));
            font-size: var(--checkbox-label-font-size, var(--text-sm));
            font-weight: var(--checkbox-label-font-weight, var(--weight-medium));
            line-height: var(--checkbox-label-line-height, var(--line-height-text-sm));
          }
        `}
  </Preview.CSS>
</Preview>

### Form Integration [#form-integration]

Use `Field` and `Fieldset` when the group needs form naming, validation state, or shared
labeling.

<Preview>
  <CheckboxGroupFormIntegrationExample />

  <Preview.Code>
    {`
          import {
            Checkbox,
            CheckboxGroup,
            CheckboxLabel,
            Field,
            FieldItem,
            FieldLabel,
            Fieldset,
            FieldsetLegend,
          } from "moduix";

          export function CheckboxGroupFormIntegrationDemo() {
            return (
              <Field name="notificationChannels">
                <Fieldset render={<CheckboxGroup defaultValue={["email"]} />}>
                  <FieldsetLegend>Notification Channels</FieldsetLegend>
                  {options.map((option) => (
                    <FieldItem key={option.value}>
                      <FieldLabel>
                        <Checkbox value={option.value} />
                        <CheckboxLabel>{option.label}</CheckboxLabel>
                      </FieldLabel>
                    </FieldItem>
                  ))}
                </Fieldset>
              </Field>
            );
          }
        `}
  </Preview.Code>

  <Preview.Data>
    {`
          const options = [
            { value: "email", label: "Email updates" },
            { value: "push", label: "Push notifications" },
            { value: "sms", label: "SMS alerts" },
          ];
        `}
  </Preview.Data>
</Preview>
