---
title: Checkbox Group
subtitle: Provides shared state to a series of checkboxes.
description: A high-quality, unstyled React checkbox group component that provides a shared state for a series of checkboxes.
---

> FineSoft Components documentation. Independent Stencil port; not the official Base UI website.
>
> React API text and `@base-ui/react` examples are upstream references. FineSoft Stencil examples use `finesoft-components`. Do not treat the two packages as interchangeable.

# Checkbox Group

A high-quality, unstyled React checkbox group component that provides a shared state for a series of checkboxes.

## Demo

### Tailwind

This example shows how to implement the component using Tailwind CSS.

```tsx
/* docs-comparison.tsx */
import { StencilComparison } from 'docs/src/components/StencilComparison';
import ReactCheckboxGroup from './react-checkbox-group';
import StencilCheckboxGroup from './stencil-checkbox-group';

export default function CheckboxGroupComparison() {
  return <StencilComparison component="checkbox-group" react={<ReactCheckboxGroup />} stencil={<StencilCheckboxGroup />} />;
}
```

```tsx
/* react-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { Checkbox } from '@base-ui/react/checkbox';
import { CheckboxGroup } from '@base-ui/react/checkbox-group';

export default function ExampleCheckboxGroup() {
  const id = React.useId();
  return (
    <CheckboxGroup aria-labelledby={id} defaultValue={['fuji-apple']} className="flex flex-col items-start gap-1 text-neutral-950 dark:text-white">
      <div className="text-sm font-bold" id={id}>
        Apples
      </div>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <Checkbox.Root
          name="apple"
          value="fuji-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <Checkbox.Indicator className="flex data-unchecked:hidden">
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Fuji
      </label>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <Checkbox.Root
          name="apple"
          value="gala-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <Checkbox.Indicator className="flex data-unchecked:hidden">
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Gala
      </label>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <Checkbox.Root
          name="apple"
          value="granny-smith-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <Checkbox.Indicator className="flex data-unchecked:hidden">
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Granny Smith
      </label>
    </CheckboxGroup>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}
```

```tsx
/* stencil-checkbox-group.tsx */
import type * as React from 'react';

export default function StencilCheckboxGroup() {
  const id = 'stencil-apples-tailwind';
  return (
    <fs-checkbox-group aria-labelledby={id} default-value='["fuji-apple"]' className="flex flex-col items-start gap-1 text-neutral-950 dark:text-white">
      <div className="text-sm font-bold" id={id}>
        Apples
      </div>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <fs-checkbox-root
          name="apple"
          value="fuji-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <fs-checkbox-indicator className="[&::part(indicator)]:box-border [&::part(indicator)]:flex [&[data-unchecked]::part(indicator)]:hidden">
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Fuji
      </label>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <fs-checkbox-root
          name="apple"
          value="gala-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <fs-checkbox-indicator className="[&::part(indicator)]:box-border [&::part(indicator)]:flex [&[data-unchecked]::part(indicator)]:hidden">
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Gala
      </label>

      <label className="flex items-center gap-2 text-sm font-normal text-neutral-950 dark:text-white">
        <fs-checkbox-root
          name="apple"
          value="granny-smith-apple"
          className="flex size-4 shrink-0 items-center justify-center border rounded-none p-0 border-neutral-950 bg-white text-white dark:border-white dark:bg-neutral-950 dark:text-neutral-950 data-checked:bg-neutral-950 data-checked:text-white dark:data-checked:bg-white dark:data-checked:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-950 dark:focus-visible:outline-white"
        >
          <fs-checkbox-indicator className="[&::part(indicator)]:box-border [&::part(indicator)]:flex [&[data-unchecked]::part(indicator)]:hidden">
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Granny Smith
      </label>
    </fs-checkbox-group>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}
```

### CSS Modules

This example shows how to implement the component using CSS Modules.

```css
/* index.module.css */
.CheckboxGroup {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.25rem;
  color: oklch(14.5% 0 0deg);

  @media (prefers-color-scheme: dark) {
    color: white;
  }
}

.Caption {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 700;
}

.Item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 400;
}

.Checkbox {
  box-sizing: border-box;
  display: flex;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  align-items: center;
  justify-content: center;
  border: 1px solid oklch(14.5% 0 0deg);
  border-radius: 0;
  background-color: white;
  color: white;
  padding: 0;
  margin: 0;

  @media (prefers-color-scheme: dark) {
    border: 1px solid white;
    background-color: oklch(14.5% 0 0deg);
    color: oklch(14.5% 0 0deg);
  }

  &[data-checked],
  &[data-indeterminate] {
    background-color: oklch(14.5% 0 0deg);
    color: white;

    @media (prefers-color-scheme: dark) {
      background-color: white;
      color: oklch(14.5% 0 0deg);
    }
  }

  &:focus-visible {
    outline: 2px solid oklch(14.5% 0 0deg);
    outline-offset: 2px;

    @media (prefers-color-scheme: dark) {
      outline-color: white;
    }
  }
}

.Indicator {
  display: flex;

  &[data-unchecked] {
    display: none;
  }
}
```

```tsx
/* docs-comparison.tsx */
import { StencilComparison } from 'docs/src/components/StencilComparison';
import ReactCheckboxGroup from './react-checkbox-group';
import StencilCheckboxGroup from './stencil-checkbox-group';

export default function CheckboxGroupComparison() {
  return <StencilComparison component="checkbox-group" react={<ReactCheckboxGroup />} stencil={<StencilCheckboxGroup />} />;
}
```

```tsx
/* react-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { Checkbox } from '@base-ui/react/checkbox';
import { CheckboxGroup } from '@base-ui/react/checkbox-group';
import styles from './index.module.css';

export default function ExampleCheckboxGroup() {
  const id = React.useId();
  return (
    <CheckboxGroup aria-labelledby={id} defaultValue={['fuji-apple']} className={styles.CheckboxGroup}>
      <div className={styles.Caption} id={id}>
        Apples
      </div>

      <label className={styles.Item}>
        <Checkbox.Root name="apple" value="fuji-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Fuji
      </label>

      <label className={styles.Item}>
        <Checkbox.Root name="apple" value="gala-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Gala
      </label>

      <label className={styles.Item}>
        <Checkbox.Root name="apple" value="granny-smith-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Granny Smith
      </label>
    </CheckboxGroup>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}
```

```tsx
/* stencil-checkbox-group.tsx */
import type * as React from 'react';
import './stencil.css';

export default function StencilCheckboxGroup() {
  const id = 'stencil-apples-css-modules';
  return (
    <fs-checkbox-group aria-labelledby={id} default-value='["fuji-apple"]'>
      <div className="demo-checkbox-group-hero-caption" id={id}>
        Apples
      </div>

      <label>
        <fs-checkbox-root name="apple" value="fuji-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Fuji
      </label>

      <label>
        <fs-checkbox-root name="apple" value="gala-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Gala
      </label>

      <label>
        <fs-checkbox-root name="apple" value="granny-smith-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Granny Smith
      </label>
    </fs-checkbox-group>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}
```

```css
/* stencil.css */
/* Example layout and visual exceptions. Default component styles come from the theme. */
@layer fs-theme {
  .demo-checkbox-group-hero-caption {
    font-size: var(--fs-font-size-sm);
    line-height: var(--fs-line-height-sm);
    font-weight: var(--fs-font-weight-bold);
  }
}
```

## Usage guidelines

- **Form controls must have an accessible name**: It can be created using `<label>` elements, or the `Field` and `Fieldset` components. See [Labeling a checkbox group](/react/components/checkbox-group.md) and the [forms guide](/react/handbook/forms.md).

## Anatomy

Checkbox Group is composed together with [Checkbox](/react/components/checkbox.md). Import the components and place them together:

```jsx title="Anatomy"
import { Checkbox } from '@base-ui/react/checkbox';
import { CheckboxGroup } from '@base-ui/react/checkbox-group';

<CheckboxGroup>
  <Checkbox.Root />
</CheckboxGroup>;
```

## Examples

### Labeling a checkbox group

Label the group with `aria-labelledby` and a sibling label element:

```tsx title="Using aria-labelledby to label a checkbox group"
<div id="protocols-label">Allowed network protocols</div>
<CheckboxGroup aria-labelledby="protocols-label">{/* ... */}</CheckboxGroup>
```

An enclosing `<label>` is the simplest labeling pattern for each checkbox:

```tsx title="Using an enclosing label to label a checkbox"
// @highlight
<label>
  <Checkbox.Root value="http" />
  HTTP
  {/* @highlight */}
</label>
```

### Rendering as a native button

By default, `<Checkbox.Root>` renders a `<span>` element to support enclosing labels. Prefer rendering each checkbox as a native button when using sibling labels (`htmlFor`/`id`).

```tsx title="Sibling label pattern with a native button"
<div id="protocols-label">Allowed network protocols</div>
<CheckboxGroup aria-labelledby="protocols-label">
  <div>
    <label htmlFor="protocol-http">HTTP</label>
    {/* @highlight-text "nativeButton" "render={<button />}" */}
    <Checkbox.Root id="protocol-http" value="http" nativeButton render={<button />}>
      <Checkbox.Indicator />
    </Checkbox.Root>
  </div>
</CheckboxGroup>
```

Native buttons with wrapping labels are supported by using the `render` callback to avoid invalid HTML, so the hidden input is placed outside the label:

```tsx title="Render callback"
<div id="protocols-label">Allowed network protocols</div>
<CheckboxGroup aria-labelledby="protocols-label">
  <Checkbox.Root
    value="http"
    nativeButton
    // @highlight-start
    render={(buttonProps) => (
      <label>
        <button {...buttonProps} />
        HTTP
      </label>
    )}
    {/* @highlight-end */}
  />
</CheckboxGroup>
```

### Form integration

Use [Field](/react/components/field.md) and [Fieldset](/react/components/fieldset.md) for group labeling and form integration:

```tsx title="Using Checkbox Group in a form"
<Form>
  {/* @highlight */}
  <Field.Root name="allowedNetworkProtocols">
    <Fieldset.Root render={<CheckboxGroup />}>
      <Fieldset.Legend>Allowed network protocols</Fieldset.Legend>
      <Field.Item>
        <Field.Label>
          <Checkbox.Root value="http" />
          HTTP
        </Field.Label>
      </Field.Item>
      <Field.Item>
        <Field.Label>
          <Checkbox.Root value="https" />
          HTTPS
        </Field.Label>
      </Field.Item>
      <Field.Item>
        <Field.Label>
          <Checkbox.Root value="ssh" />
          SSH
        </Field.Label>
      </Field.Item>
    </Fieldset.Root>
  </Field.Root>
</Form>
```

### Parent checkbox

A checkbox that controls other checkboxes within a `<CheckboxGroup>` can be created:

1. Make `<CheckboxGroup>` a controlled component
2. Pass an array of all the child checkbox values to the `allValues` prop on the `<CheckboxGroup>` component
3. Add the `parent` boolean prop to the parent `<Checkbox.Root>`

The group controls the parent checkbox's [indeterminate](/react/components/checkbox.md) state when some, but not all, child checkboxes are checked.

## Demo

### CSS Modules

This example shows how to implement the component using CSS Modules.

```css
/* index.module.css */
.CheckboxGroup {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.25rem;
  color: oklch(14.5% 0 0deg);

  @media (prefers-color-scheme: dark) {
    color: white;
  }
}

.Caption {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 700;
}

.Item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 400;
}

.Checkbox {
  box-sizing: border-box;
  display: flex;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  align-items: center;
  justify-content: center;
  border: 1px solid oklch(14.5% 0 0deg);
  border-radius: 0;
  background-color: white;
  color: white;
  padding: 0;
  margin: 0;

  @media (prefers-color-scheme: dark) {
    border: 1px solid white;
    background-color: oklch(14.5% 0 0deg);
    color: oklch(14.5% 0 0deg);
  }

  &[data-checked],
  &[data-indeterminate] {
    background-color: oklch(14.5% 0 0deg);
    color: white;

    @media (prefers-color-scheme: dark) {
      background-color: white;
      color: oklch(14.5% 0 0deg);
    }
  }

  &:focus-visible {
    outline: 2px solid oklch(14.5% 0 0deg);
    outline-offset: 2px;

    @media (prefers-color-scheme: dark) {
      outline-color: white;
    }
  }
}

.Indicator {
  display: flex;

  &[data-unchecked] {
    display: none;
  }
}
```

```tsx
/* docs-comparison.tsx */
import { StencilComparison } from 'docs/src/components/StencilComparison';
import ReactCheckboxGroup from './react-checkbox-group';
import StencilCheckboxGroup from './stencil-checkbox-group';
import { StencilCheckboxGroupView } from './stencil-checkbox-group-view';

export default function CheckboxGroupComparison() {
  return <StencilComparison component="checkbox-group" react={<ReactCheckboxGroup />} stencil={<StencilCheckboxGroupView />} clientStencil={<StencilCheckboxGroup />} />;
}
```

```tsx
/* react-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { Checkbox } from '@base-ui/react/checkbox';
import { CheckboxGroup } from '@base-ui/react/checkbox-group';
import styles from './index.module.css';

const fruits = ['fuji-apple', 'gala-apple', 'granny-smith-apple'];

export default function ExampleCheckboxGroup() {
  const id = React.useId();
  const [value, setValue] = React.useState<string[]>([]);

  return (
    <CheckboxGroup aria-labelledby={id} value={value} onValueChange={setValue} allValues={fruits} className={styles.CheckboxGroup} style={{ marginLeft: '1rem' }}>
      <label className={styles.Item} id={id} style={{ marginLeft: '-1rem' }}>
        <Checkbox.Root className={styles.Checkbox} parent>
          <Checkbox.Indicator className={styles.Indicator} render={(props, state) => <span {...props}>{state.indeterminate ? <HorizontalRuleIcon /> : <CheckIcon />}</span>} />
        </Checkbox.Root>
        Apples
      </label>

      <label className={styles.Item}>
        <Checkbox.Root value="fuji-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Fuji
      </label>

      <label className={styles.Item}>
        <Checkbox.Root value="gala-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Gala
      </label>

      <label className={styles.Item}>
        <Checkbox.Root value="granny-smith-apple" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Granny Smith
      </label>
    </CheckboxGroup>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}

function HorizontalRuleIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" strokeWidth={1} {...props} style={{ display: 'block', ...props.style }}>
      <line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}
```

```tsx
/* stencil-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { flushSync } from 'react-dom';
import { StencilCheckboxGroupView } from './stencil-checkbox-group-view';

export default function StencilCheckboxGroup() {
  const id = React.useId();
  const [value, setValue] = React.useState<string[]>([]);
  return (
    <StencilCheckboxGroupView
      id={id}
      value={value}
      onValueChange={event => {
        // Commit this native custom event before another checkbox receives input.
        flushSync(() => setValue(event.detail.value));
      }}
    />
  );
}
```

```tsx
/* stencil-checkbox-group-view.tsx */
import type * as React from 'react';
import type { CheckboxGroupValueChangeDetail } from 'finesoft-components/define-custom-elements';
import './stencil.css';
import './stencil-layout.css';

export const fruits = ['fuji-apple', 'gala-apple', 'granny-smith-apple'];

export function StencilCheckboxGroupView({
  value = [],
  id = 'stencil-apples-parent',
  onValueChange,
}: {
  value?: string[];
  id?: string;
  onValueChange?: (event: CustomEvent<CheckboxGroupValueChangeDetail>) => void;
}) {
  return (
    <fs-checkbox-group aria-labelledby={id} value={JSON.stringify(value)} onvalueChange={onValueChange} all-values={JSON.stringify(fruits)} style={{ marginLeft: '1rem' }}>
      <label id={id} style={{ marginLeft: '-1rem' }}>
        <fs-checkbox-root parent>
          <fs-checkbox-indicator className="demo-checkbox-group-parent-layout-parent-indicator">
            <CheckIcon />
            <HorizontalRuleIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Apples
      </label>

      <label>
        <fs-checkbox-root value="fuji-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Fuji
      </label>

      <label>
        <fs-checkbox-root value="gala-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Gala
      </label>

      <label>
        <fs-checkbox-root value="granny-smith-apple">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Granny Smith
      </label>
    </fs-checkbox-group>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}

function HorizontalRuleIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" strokeWidth={1} {...props} style={{ display: 'block', ...props.style }}>
      <line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}
```

```css
/* stencil.css */
/* Component skin comes from the optional theme; icon selection is in stencil-layout.css. */
```

```css
/* stencil-layout.css */
/* Example layout and visual exceptions. Default component styles come from the theme. */
@layer fs-theme {
  .demo-checkbox-group-parent-layout-parent-indicator[data-indeterminate] > svg:first-of-type,
  .demo-checkbox-group-parent-layout-parent-indicator:not([data-indeterminate]) > svg:last-of-type {
    display: none !important;
  }
}
```

### Nested parent checkbox

## Demo

### CSS Modules

This example shows how to implement the component using CSS Modules.

```css
/* index.module.css */
.CheckboxGroup {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.25rem;
  color: oklch(14.5% 0 0deg);

  @media (prefers-color-scheme: dark) {
    color: white;
  }
}

.Caption {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 700;
}

.Item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 400;
}

.Checkbox {
  box-sizing: border-box;
  display: flex;
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  align-items: center;
  justify-content: center;
  border: 1px solid oklch(14.5% 0 0deg);
  border-radius: 0;
  background-color: white;
  color: white;
  padding: 0;
  margin: 0;

  @media (prefers-color-scheme: dark) {
    border: 1px solid white;
    background-color: oklch(14.5% 0 0deg);
    color: oklch(14.5% 0 0deg);
  }

  &[data-checked],
  &[data-indeterminate] {
    background-color: oklch(14.5% 0 0deg);
    color: white;

    @media (prefers-color-scheme: dark) {
      background-color: white;
      color: oklch(14.5% 0 0deg);
    }
  }

  &:focus-visible {
    outline: 2px solid oklch(14.5% 0 0deg);
    outline-offset: 2px;

    @media (prefers-color-scheme: dark) {
      outline-color: white;
    }
  }
}

.Indicator {
  display: flex;

  &[data-unchecked] {
    display: none;
  }
}
```

```tsx
/* docs-comparison.tsx */
import { StencilComparison } from 'docs/src/components/StencilComparison';
import ReactCheckboxGroup from './react-checkbox-group';
import StencilCheckboxGroup from './stencil-checkbox-group';
import { StencilCheckboxGroupView } from './stencil-checkbox-group-view';

export default function CheckboxGroupComparison() {
  return <StencilComparison component="checkbox-group" react={<ReactCheckboxGroup />} stencil={<StencilCheckboxGroupView />} clientStencil={<StencilCheckboxGroup />} />;
}
```

```tsx
/* react-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { Checkbox } from '@base-ui/react/checkbox';
import { CheckboxGroup } from '@base-ui/react/checkbox-group';
import styles from './index.module.css';

const mainPermissions = ['view-dashboard', 'manage-users', 'access-reports'];
const userManagementPermissions = ['create-user', 'edit-user', 'delete-user', 'assign-roles'];

export default function PermissionsForm() {
  const id = React.useId();
  const [mainValue, setMainValue] = React.useState<string[]>([]);
  const [managementValue, setManagementValue] = React.useState<string[]>([]);

  return (
    <CheckboxGroup
      aria-labelledby={id}
      value={mainValue}
      onValueChange={value => {
        if (value.includes('manage-users')) {
          setManagementValue(userManagementPermissions);
        } else if (managementValue.length === userManagementPermissions.length) {
          setManagementValue([]);
        }
        setMainValue(value);
      }}
      allValues={mainPermissions}
      className={styles.CheckboxGroup}
      style={{ marginLeft: '1rem' }}
    >
      <label className={styles.Item} id={id} style={{ marginLeft: '-1rem' }}>
        <Checkbox.Root className={styles.Checkbox} parent indeterminate={managementValue.length > 0 && managementValue.length !== userManagementPermissions.length}>
          <Checkbox.Indicator className={styles.Indicator} render={(props, state) => <span {...props}>{state.indeterminate ? <HorizontalRuleIcon /> : <CheckIcon />}</span>} />
        </Checkbox.Root>
        User Permissions
      </label>

      <label className={styles.Item}>
        <Checkbox.Root value="view-dashboard" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        View Dashboard
      </label>

      <label className={styles.Item}>
        <Checkbox.Root value="access-reports" className={styles.Checkbox}>
          <Checkbox.Indicator className={styles.Indicator}>
            <CheckIcon />
          </Checkbox.Indicator>
        </Checkbox.Root>
        Access Reports
      </label>

      <CheckboxGroup
        aria-labelledby="manage-users-caption"
        className={styles.CheckboxGroup}
        value={managementValue}
        onValueChange={value => {
          if (value.length === userManagementPermissions.length) {
            setMainValue(prev => Array.from(new Set([...prev, 'manage-users'])));
          } else {
            setMainValue(prev => prev.filter(v => v !== 'manage-users'));
          }
          setManagementValue(value);
        }}
        allValues={userManagementPermissions}
        style={{ marginLeft: '1rem' }}
      >
        <label className={styles.Item} id="manage-users-caption" style={{ marginLeft: '-1rem' }}>
          <Checkbox.Root className={styles.Checkbox} parent>
            <Checkbox.Indicator className={styles.Indicator} render={(props, state) => <span {...props}>{state.indeterminate ? <HorizontalRuleIcon /> : <CheckIcon />}</span>} />
          </Checkbox.Root>
          Manage Users
        </label>

        <label className={styles.Item}>
          <Checkbox.Root value="create-user" className={styles.Checkbox}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Root>
          Create User
        </label>

        <label className={styles.Item}>
          <Checkbox.Root value="edit-user" className={styles.Checkbox}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Root>
          Edit User
        </label>

        <label className={styles.Item}>
          <Checkbox.Root value="delete-user" className={styles.Checkbox}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Root>
          Delete User
        </label>

        <label className={styles.Item}>
          <Checkbox.Root value="assign-roles" className={styles.Checkbox}>
            <Checkbox.Indicator className={styles.Indicator}>
              <CheckIcon />
            </Checkbox.Indicator>
          </Checkbox.Root>
          Assign Roles
        </label>
      </CheckboxGroup>
    </CheckboxGroup>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}

function HorizontalRuleIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" strokeWidth={1} {...props} style={{ display: 'block', ...props.style }}>
      <line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}
```

```tsx
/* stencil-checkbox-group.tsx */
'use client';
import * as React from 'react';
import { flushSync } from 'react-dom';
import type { CheckboxGroupValueChangeDetail } from 'finesoft-components/define-custom-elements';
import { StencilCheckboxGroupView, userManagementPermissions } from './stencil-checkbox-group-view';

export default function StencilCheckboxGroup() {
  const id = React.useId();
  const [mainValue, setMainValue] = React.useState<string[]>([]);
  const [managementValue, setManagementValue] = React.useState<string[]>([]);

  const handleMainValueChange = (event: CustomEvent<CheckboxGroupValueChangeDetail>) => {
    // Native custom events bubble; only handle this group's own request here.
    if (event.target !== event.currentTarget) return;
    const value = event.detail.value;
    // Commit both groups together before the next native input event.
    flushSync(() => {
      if (value.includes('manage-users')) {
        setManagementValue(userManagementPermissions);
      } else if (managementValue.length === userManagementPermissions.length) {
        setManagementValue([]);
      }
      setMainValue(value);
    });
  };

  const handleManagementValueChange = (event: CustomEvent<CheckboxGroupValueChangeDetail>) => {
    const value = event.detail.value;
    flushSync(() => {
      if (value.length === userManagementPermissions.length) {
        setMainValue(prev => Array.from(new Set([...prev, 'manage-users'])));
      } else {
        setMainValue(prev => prev.filter(item => item !== 'manage-users'));
      }
      setManagementValue(value);
    });
  };

  return (
    <StencilCheckboxGroupView
      id={id}
      mainValue={mainValue}
      managementValue={managementValue}
      onMainValueChange={handleMainValueChange}
      onManagementValueChange={handleManagementValueChange}
    />
  );
}
```

```tsx
/* stencil-checkbox-group-view.tsx */
import type * as React from 'react';
import type { CheckboxGroupValueChangeDetail } from 'finesoft-components/define-custom-elements';
import './stencil.css';
import './stencil-layout.css';

export const mainPermissions = ['view-dashboard', 'manage-users', 'access-reports'];
export const userManagementPermissions = ['create-user', 'edit-user', 'delete-user', 'assign-roles'];

export function StencilCheckboxGroupView({
  mainValue = [],
  managementValue = [],
  id = 'stencil-permissions',
  onMainValueChange,
  onManagementValueChange,
}: {
  mainValue?: string[];
  managementValue?: string[];
  id?: string;
  onMainValueChange?: (event: CustomEvent<CheckboxGroupValueChangeDetail>) => void;
  onManagementValueChange?: (event: CustomEvent<CheckboxGroupValueChangeDetail>) => void;
}) {
  return (
    <fs-checkbox-group
      aria-labelledby={id}
      value={JSON.stringify(mainValue)}
      onvalueChange={onMainValueChange}
      all-values={JSON.stringify(mainPermissions)}
      style={{ marginLeft: '1rem' }}
    >
      <label id={id} style={{ marginLeft: '-1rem' }}>
        <fs-checkbox-root parent indeterminate={managementValue.length > 0 && managementValue.length !== userManagementPermissions.length}>
          <fs-checkbox-indicator className="demo-checkbox-group-nested-layout-parent-indicator">
            <CheckIcon />
            <HorizontalRuleIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        User Permissions
      </label>

      <label>
        <fs-checkbox-root value="view-dashboard">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        View Dashboard
      </label>

      <label>
        <fs-checkbox-root value="access-reports">
          <fs-checkbox-indicator>
            <CheckIcon />
          </fs-checkbox-indicator>
        </fs-checkbox-root>
        Access Reports
      </label>

      <fs-checkbox-group
        aria-labelledby={`${id}-manage-users-caption`}
        value={JSON.stringify(managementValue)}
        onvalueChange={onManagementValueChange}
        all-values={JSON.stringify(userManagementPermissions)}
        style={{ marginLeft: '1rem' }}
      >
        <label id={`${id}-manage-users-caption`} style={{ marginLeft: '-1rem' }}>
          <fs-checkbox-root parent>
            <fs-checkbox-indicator className="demo-checkbox-group-nested-layout-parent-indicator">
              <CheckIcon />
              <HorizontalRuleIcon />
            </fs-checkbox-indicator>
          </fs-checkbox-root>
          Manage Users
        </label>

        <label>
          <fs-checkbox-root value="create-user">
            <fs-checkbox-indicator>
              <CheckIcon />
            </fs-checkbox-indicator>
          </fs-checkbox-root>
          Create User
        </label>

        <label>
          <fs-checkbox-root value="edit-user">
            <fs-checkbox-indicator>
              <CheckIcon />
            </fs-checkbox-indicator>
          </fs-checkbox-root>
          Edit User
        </label>

        <label>
          <fs-checkbox-root value="delete-user">
            <fs-checkbox-indicator>
              <CheckIcon />
            </fs-checkbox-indicator>
          </fs-checkbox-root>
          Delete User
        </label>

        <label>
          <fs-checkbox-root value="assign-roles">
            <fs-checkbox-indicator>
              <CheckIcon />
            </fs-checkbox-indicator>
          </fs-checkbox-root>
          Assign Roles
        </label>
      </fs-checkbox-group>
    </fs-checkbox-group>
  );
}

function CheckIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" {...props} style={{ display: 'block', ...props.style }}>
      <path d="m2.5 8.5 4 4 7-9" />
    </svg>
  );
}

function HorizontalRuleIcon(props: React.ComponentProps<'svg'>) {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" strokeWidth={1} {...props} style={{ display: 'block', ...props.style }}>
      <line x1="3" y1="12" x2="21" y2="12" stroke="currentColor" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}
```

```css
/* stencil.css */
/* Component skin comes from the optional theme; icon selection is in stencil-layout.css. */
```

```css
/* stencil-layout.css */
/* Example layout and visual exceptions. Default component styles come from the theme. */
@layer fs-theme {
  .demo-checkbox-group-nested-layout-parent-indicator[data-indeterminate] > svg:first-of-type,
  .demo-checkbox-group-nested-layout-parent-indicator:not([data-indeterminate]) > svg:last-of-type {
    display: none !important;
  }
}
```

## API reference

### CheckboxGroup

Provides a shared state to a series of checkboxes.

**CheckboxGroup Props:**

| Prop          | Type                                                                                        | Default | Description                                                                                                                                                                                   |
| :------------ | :------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| defaultValue  | `string[]`                                                                                  | -       | Names of the checkboxes in the group that should be initially ticked. To render a controlled checkbox group, use the `value` prop instead.                                                    |
| value         | `string[]`                                                                                  | -       | Names of the checkboxes in the group that should be ticked. To render an uncontrolled checkbox group, use the `defaultValue` prop instead.                                                    |
| onValueChange | `((value: string[], eventDetails: CheckboxGroup.ChangeEventDetails) => void)`               | -       | Event handler called when a checkbox in the group is ticked or unticked.&#xA;Provides the new value as an argument.                                                                           |
| allValues     | `string[]`                                                                                  | -       | Names of all checkboxes in the group. Use this when creating a parent checkbox.                                                                                                               |
| disabled      | `boolean`                                                                                   | `false` | Whether the component should ignore user interaction.                                                                                                                                         |
| className     | `string \| ((state: CheckboxGroup.State) => string \| undefined)`                           | -       | CSS class applied to the element, or a function that&#xA;returns a class based on the component's state.                                                                                      |
| style         | `React.CSSProperties \| ((state: CheckboxGroup.State) => React.CSSProperties \| undefined)` | -       | Style applied to the element, or a function that&#xA;returns a style object based on the component's state.                                                                                   |
| render        | `ReactElement \| ((props: HTMLProps, state: CheckboxGroup.State) => ReactElement)`          | -       | Allows you to replace the component's HTML element&#xA;with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

**CheckboxGroup Data Attributes:**

| Attribute     | Type | Description                                  |
| :------------ | :--- | :------------------------------------------- |
| data-disabled | -    | Present when the checkbox group is disabled. |

### CheckboxGroup.Props

Re-export of [CheckboxGroup](/react/components/checkbox-group.md) props.

### CheckboxGroup.State

```typescript
type CheckboxGroupState = {
  /** Whether the component should ignore user interaction. */
  disabled: boolean;
  /** Whether the field has been touched. */
  touched: boolean;
  /** Whether the field value has changed from its initial value. */
  dirty: boolean;
  /** Whether the field is valid. */
  valid: boolean | null;
  /** Whether the field has a value. */
  filled: boolean;
  /** Whether the field is focused. */
  focused: boolean;
};
```

### CheckboxGroup.ChangeEventReason

```typescript
type CheckboxGroupChangeEventReason = 'none';
```

### CheckboxGroup.ChangeEventDetails

```typescript
type CheckboxGroupChangeEventDetails = {
  /** The reason for the event. */
  reason: 'none';
  /** The native event associated with the custom event. */
  event: Event;
  /** Cancels Base UI from handling the event. */
  cancel: () => void;
  /** Allows the event to propagate in cases where Base UI will stop the propagation. */
  allowPropagation: () => void;
  /** Indicates whether the event has been canceled. */
  isCanceled: boolean;
  /** Indicates whether the event is allowed to propagate. */
  isPropagationAllowed: boolean;
  /** The element that triggered the event, if applicable. */
  trigger: Element | undefined;
};
```

## Canonical Types

Maps `Canonical`: `Alias` — Use Canonical when its namespace is already imported; otherwise use Alias.

- `CheckboxGroup.State`: `CheckboxGroupState`
- `CheckboxGroup.Props`: `CheckboxGroupProps`
- `CheckboxGroup.ChangeEventReason`: `CheckboxGroupChangeEventReason`
- `CheckboxGroup.ChangeEventDetails`: `CheckboxGroupChangeEventDetails`
