Checkbox Group

Checkbox Groups combine a series of checkboxes together.

Give FeedbackWAI-ARIABundle Size
'use client';
import * as React from 'react';
import { Checkbox } from '@base_ui/react/Checkbox';
import { CheckboxGroup } from '@base_ui/react/CheckboxGroup';
import { Field } from '@base_ui/react/Field';

export default function UnstyledCheckboxIndeterminateGroup() {
  return (
    <Field.Root>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <CheckboxGroup.Root defaultValue={['red']}>
          <Field.Label className="CheckboxGroup-label">Colors</Field.Label>
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            <Field.Root>
              <Checkbox.Root className="Checkbox" name="red">
                <Checkbox.Indicator className="Checkbox-indicator">
                  <CheckIcon className="Check" />
                </Checkbox.Indicator>
              </Checkbox.Root>
              <Field.Label className="Checkbox-label">Red</Field.Label>
            </Field.Root>
            <Field.Root>
              <Checkbox.Root className="Checkbox" name="green">
                <Checkbox.Indicator className="Checkbox-indicator">
                  <CheckIcon className="Check" />
                </Checkbox.Indicator>
              </Checkbox.Root>
              <Field.Label className="Checkbox-label">Green</Field.Label>
            </Field.Root>
            <Field.Root>
              <Checkbox.Root className="Checkbox" name="blue">
                <Checkbox.Indicator className="Checkbox-indicator">
                  <CheckIcon className="Check" />
                </Checkbox.Indicator>
              </Checkbox.Root>
              <Field.Label className="Checkbox-label">Blue</Field.Label>
            </Field.Root>
          </div>
        </CheckboxGroup.Root>
        <Styles />
      </div>
    </Field.Root>
  );
}

const grey = {
  100: '#E5EAF2',
  300: '#C7D0DD',
  500: '#9DA8B7',
  600: '#6B7A90',
  800: '#303740',
  900: '#1C2025',
};

function Styles() {
  return (
    <style>
      {`
      .Check {
        height: 100%;
        width: 100%;
      }

      .CheckboxGroup-label {
        display: flex;
        font-weight: bold;
        gap: 8px;
        margin-bottom: 8px;
        font-size: 17px;
      }

      .Checkbox-label {
        display: flex;
        font-weight: 500;
        gap: 8px;
        margin-bottom: 8px;
      }

      .Checkbox {
        all: unset;
        box-sizing: border-box;
        text-align: center;
        width: 24px;
        height: 24px;
        padding: 0;
        border-radius: 4px;
        border: 2px solid ${grey[600]};
        background: none;
        transition-property: background, border-color;
        transition-duration: 0.15s;
      }

      .Checkbox[data-disabled] {
        opacity: 0.4;
        cursor: not-allowed;
      }

      .Checkbox:focus-visible {
        outline: 2px solid ${grey[500]};
        outline-offset: 2px;
      }

      .Checkbox[data-checked] {
        border-color: ${grey[800]};
        background: ${grey[800]};
      }

      .Checkbox-indicator {
        height: 100%;
        display: inline-block;
        visibility: hidden;
        color: ${grey[100]};
      }

      .Checkbox-indicator[data-checked] {
        visibility: visible;
      }

      .Checkbox-icon {
        width: 100%;
        height: 100%;
      }

      @media (prefers-color-scheme: dark) {
        .Checkbox {
          border-color: ${grey[500]};
        }

        .Checkbox:focus-visible {
          outline: 2px solid ${grey[600]};
          outline-offset: 2px;
        }

        .Checkbox[data-checked] {
          border-color: ${grey[300]};
          background: ${grey[300]};
        }

        .Checkbox:hover:not([data-disabled]) {
          border-color: ${grey[100]};
        }

        .Checkbox-indicator {
          color: ${grey[900]};
        }
      }
    `}
    </style>
  );
}

function CheckIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      {...props}
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
    >
      <path
        d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
        fill="currentColor"
      />
    </svg>
  );
}

Installation

Base UI components are all available as a single package.

npm install @base_ui/react

Once you have the package installed, import the component.

import { CheckboxGroup } from '@base_ui/react/CheckboxGroup';
import { Checkbox } from '@base_ui/react/Checkbox';

Anatomy

Checkbox Group is composed of a Root component and Checkbox components:

<CheckboxGroup.Root>
  <Checkbox.Root />
  <Checkbox.Root />
</CheckboxGroup.Root>

Labeling

Field components are used to label the Checkbox Group and individual Checkboxes:

import { Field } from '@base_ui/react/Field';
<Field.Root>
  <CheckboxGroup.Root>
    <Field.Label>Colors</Field.Label>
    <Field.Root>
      <Checkbox.Root name="red" />
      <Field.Label>Red</Field.Label>
    </Field.Root>
    <Field.Root>
      <Checkbox.Root name="blue" />
      <Field.Label>Blue</Field.Label>
    </Field.Root>
  </CheckboxGroup.Root>
</Field.Root>

Controlled

The value and onValueChange props control the Checkbox Group with external state. value is an array of strings matching the name props of Checkboxes that are currently checked:

const [value, setValue] = useState(['red']);
 
return (
  <CheckboxGroup.Root value={value} onValueChange={setValue}>
    <Checkbox.Root name="red" /> {/* Checked */}
    <Checkbox.Root name="green" />
    <Checkbox.Root name="blue" />
  </CheckboxGroup.Root>
);

Parent Checkbox

A Checkbox can control a group of child Checkboxes.

  1. Make CheckboxGroup.Root controlled and add allValues as a prop — an array of strings that contains the names of all the child checkboxes.
  2. Add a parent prop to the Checkbox.Root component that controls the other (child) Checkboxes inside the group.
  3. Give the child Checkboxes a name prop that identifies them inside the allValues array.
const allValues = ['a', 'b', 'c'];
 
function App() {
  const [value, setValue] = useState([]);
  return (
    <CheckboxGroup.Root value={value} onValueChange={setValue} allValues={allValues}>
      <Checkbox.Root parent />
      {allValues.map((value) => (
        <Checkbox.Root key={value} name={value} />
      ))}
    </CheckboxGroup.Root>
  );
}
'use client';
import * as React from 'react';
import { Checkbox as BaseCheckbox } from '@base_ui/react/Checkbox';
import { CheckboxGroup } from '@base_ui/react/CheckboxGroup';
import { Field } from '@base_ui/react/Field';
import { styled } from '@mui/system';

const colors = ['red', 'green', 'blue'];

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

  return (
    <Field.Root style={{ display: 'flex', flexDirection: 'column' }}>
      <CheckboxGroup.Root
        allValues={colors}
        value={value}
        onValueChange={setValue}
        preserveChildStates={false}
      >
        <CheckboxGroupLabel>Colors</CheckboxGroupLabel>
        <FieldRoot render={<ul />}>
          <Checkbox parent>
            <Indicator
              render={(props, { indeterminate }) => (
                <span {...props}>
                  {indeterminate ? <HorizontalRuleIcon /> : <CheckIcon />}
                </span>
              )}
            />
          </Checkbox>
          <CheckboxLabel>All Colors</CheckboxLabel>
        </FieldRoot>
        <List>
          {colors.map((color) => (
            <FieldListItem key={color} render={<li />}>
              <Checkbox name={color}>
                <Indicator>
                  <CheckIcon />
                </Indicator>
              </Checkbox>
              <CheckboxLabel>{color}</CheckboxLabel>
            </FieldListItem>
          ))}
        </List>
      </CheckboxGroup.Root>
    </Field.Root>
  );
}

const blue = {
  400: '#3399FF',
  600: '#0072E6',
  800: '#004C99',
};

const grey = {
  100: '#E5EAF2',
  400: '#B0B8C4',
  800: '#303740',
};

const CheckboxGroupLabel = styled(Field.Label)`
  display: block;
  font-weight: bold;
  font-size: 18px;
  margin-bottom: 8px;
`;

const Checkbox = styled(BaseCheckbox.Root)(
  ({ theme }) => `
    width: 24px;
    height: 24px;
    padding: 0;
    border-radius: 4px;
    border: 2px solid ${blue[600]};
    background: none;
    transition-property: background, border-color;
    transition-duration: 0.15s;
    outline: none;

    &[data-disabled] {
      opacity: 0.4;
      cursor: not-allowed;
    }

    &:focus-visible {
      outline: 2px solid ${theme.palette.mode === 'dark' ? blue[800] : blue[400]};
      outline-offset: 2px;
    }

    &[data-checked], &[data-indeterminate] {
      border-color: transparent;
      background: ${blue[600]};
    }
  `,
);

const HorizontalRuleIcon = styled(function HorizontalRuleIcon(
  props: React.SVGProps<SVGSVGElement>,
) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      {...props}
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
    >
      <path d="M4 11h16v2H4z" fill="currentColor" />
    </svg>
  );
})`
  height: 100%;
  width: 100%;
`;

const CheckIcon = styled(function CheckIcon(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      {...props}
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
    >
      <path
        d="M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
        fill="currentColor"
      />
    </svg>
  );
})`
  height: 100%;
  width: 100%;
`;

const Indicator = styled(BaseCheckbox.Indicator)`
  height: 100%;
  display: inline-block;
  visibility: hidden;
  color: ${grey[100]};

  &[data-checked],
  &[data-indeterminate] {
    visibility: visible;
  }
`;

const FieldRoot = styled(Field.Root)`
  display: flex;
  align-items: center;
  margin-bottom: 8px;
  padding: 0;
`;

const List = styled('ul')`
  list-style: none;
  padding: 0;
  margin: 0;
  margin-left: 32px;
`;

const FieldListItem = styled(Field.Root)`
  display: flex;
  align-items: center;

  &:not(:last-child) {
    margin-bottom: 8px;
  }
`;

const CheckboxLabel = styled(Field.Label)`
  display: flex;
  gap: 8px;
  text-transform: capitalize;
  padding-left: 8px;
`;

To preserve the initial state of the child checkboxes when the parent checkbox is toggled, set the preserveChildStates prop to true:

<CheckboxGroup.Root preserveChildStates>
  <Checkbox.Root parent />
  {allValues.map((value) => (
    <Checkbox.Root key={value} name={value} />
  ))}
</CheckboxGroup.Root>

API Reference

CheckboxGroupRoot

The foundation for building custom-styled checkbox groups.

PropTypeDefaultDescription
allValuesarrayOfAll values of the checkboxes in the group.
classNameunionClass names applied to the element or a function that returns them based on the component's state.
defaultValuearrayOfThe default checked values of the checkbox group. Use when uncontrolled.
disabledboolfalseWhether the checkbox group is disabled.
onValueChangefuncA callback function that is called when the value of the checkbox group changes. Use when controlled.
preserveChildStatesboolfalseWhether the parent checkbox should preserve its child states when checked/unchecked, leading to a tri-state checkbox group.
renderunionA function to customize rendering of the component.
valuearrayOfThe currently checked values of the checkbox group. Use when controlled.

Contents