A React checkbox group with Liquid Glass controls, labels, and descriptions.
CheckboxGroup renders a labelled set of independent checkbox choices on one shared state. Each option is a real checkbox with its own label and description, and the group value is an array of the checked values.
Options are passed through the items array. The group owns the checked values, supports controlled and uncontrolled usage, and shares one label and description across the set.
label and description name the group and explain its purpose. Set defaultValue to start with options checked, or use value with onValueChange for a controlled group.
A parent checkbox is composed from Checkbox and CheckboxGroup rather than built into the group. Keep the checked values in state, derive the parent's checked and indeterminate props from that state, and select or clear every option in onCheckedChange. The parent sits above the indented group. Pass the same size to both controls so their rows stay aligned.
Nest a second Checkbox and CheckboxGroup one level deeper. Derive each parent from its own array and the outer parent from both arrays. Selecting the outer parent updates both arrays, so repeated clicks always alternate between every option and none.
value and onValueChange let external state fully control the selection. The callback receives the next array of checked values and the Base UI event details.
Selected: comments, billing
Supports xs / sm / md / lg / xl, defaulting to md. The control, label, description, and spacing scale together.
Set disabled on the group to disable every option, or on a single item to disable only that option. readOnly keeps every checkbox focusable while preventing changes.
Set name to submit every checked value through hidden native checkboxes with the same field name. Read them with FormData.getAll(name). form links the inputs to a form elsewhere in the document.
CheckboxGroup exposes a group named by label or by aria-label / aria-labelledby. Each option is a checkbox with its visible label and description linked automatically. Space toggles the focused checkbox, and Tab moves between options because a checkbox group is not a composite widget.
A parent checkbox is a separate Checkbox: give the group its own accessible name, derive the parent's checked and indeterminate state from the shared arrays, and keep both levels in sync. Parent checkboxes are controls only, so they do not submit a value unless you give them a name and value.
Disabled options remain announced but cannot change. Each checked option submits its own hidden native checkbox; there is no native required for a group, so validate that at least one value is selected in your form handler. See the accessibility guide for form patterns.
CheckboxGroup
| Prop | Type | Default | Description |
|---|---|---|---|
items | readonly CheckboxGroupItemProps[] | — | Options rendered by the group. Required. |
label | ReactNode | — | Visible label for the group. |
description | ReactNode | — | Supporting text under the group label, linked with aria-describedby. |
value | readonly string[] | — | Controlled checked values. |
defaultValue | readonly string[] | [] | Initial checked values when uncontrolled. |
onValueChange | (value: string[], details) => void | — | Called when a checkbox is ticked or unticked. |
name | string | — | Form field name applied to every checkbox in the group. |
form | string | — | ID of the form that owns the hidden inputs. |
disabled | boolean | false | Disables every option. |
readOnly | boolean | false | Prevents changes while keeping every option focusable. |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Control, label, description, and spacing size. |
aria-label | string | — | Accessible name when no visible label is rendered. |
aria-labelledby | string | — | ID reference for an external accessible name. |
aria-describedby | string | — | ID reference for external supporting text. A visible description is merged with this value. |
render | ReactElement | ((props, state) => ReactElement) | — | Replaces the group root element. |
ref | Ref<HTMLDivElement> | — | Ref forwarded to the group root. |
className | string | — | Tailwind classes merged into the group root. |
style | CSSProperties | — | Inline styles merged into the same element as className. |
ui | { root?: ClassValue; header?: ClassValue; label?: ClassValue; description?: ClassValue; items?: ClassValue; item?: ClassValue; box?: ClassValue; fill?: ClassValue; glass?: ClassValue; overlay?: ClassValue; indicator?: ClassValue; indicatorIcon?: ClassValue; wrapper?: ClassValue; itemLabel?: ClassValue; itemDescription?: ClassValue } | — | Tailwind classes for internal slots, keyed by data-slot. |
CheckboxGroupItemProps
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Identifies the option in the group value. Required. |
indeterminate | boolean | false | Forces the mixed state. |
label | ReactNode | — | Visible label next to the control. It becomes the accessible name unless aria-label or aria-labelledby is provided. |
description | ReactNode | — | Supporting copy under the label, linked with aria-describedby when the control has a name. |
disabled | boolean | false | Disables this option. |
readOnly | boolean | false | Prevents changes while remaining focusable. |
required | boolean | false | Marks this checkbox as required for form submission. |
name | string | — | Form field name for this option. Defaults to the group name. |
form | string | — | ID of the form that owns this option's hidden input. Defaults to the group form. |
uncheckedValue | string | — | Value submitted when this option is unchecked. |
id | string | — | ID of the hidden input, or of the root element when nativeButton is true. |
inputRef | Ref<HTMLInputElement> | — | Ref to the hidden native input. |
nativeButton | boolean | false | Whether the rendered element is a native <button>. |
render | ReactElement | ((props, state) => ReactElement) | — | Replaces the entry root element. |
aria-label | string | — | Accessible name when no visible label is rendered. |
aria-labelledby | string | — | ID of an external label. |
aria-describedby | string | — | ID of external supporting text. |
ref | Ref<HTMLElement> | — | Ref forwarded to the entry root. |
className | string | — | Tailwind classes merged into the entry root. |
style | CSSProperties | — | Inline styles merged into the same element as className. |
ui | { item?: ClassValue; box?: ClassValue; fill?: ClassValue; glass?: ClassValue; overlay?: ClassValue; indicator?: ClassValue; indicatorIcon?: ClassValue; wrapper?: ClassValue; itemLabel?: ClassValue; itemDescription?: ClassValue } | — | Tailwind classes for control slots, keyed by data-slot. |
Parent checkboxes are not part of the component API. Compose them with Checkbox and keep the arrays in sync, as shown above.
ui keys map to the data-slot values listed below. All other Base UI CheckboxGroup props are forwarded. Each item also forwards the remaining Base UI Checkbox props.
| Attribute | Description |
|---|---|
data-disabled | Present when the group or an option is disabled. |
data-readonly | Present when an option is read-only. |
data-required | Present when an option is required. |
data-checked | Present on a checked option. |
data-unchecked | Present on an unchecked option. |
data-indeterminate | Present when an option is mixed. |
data-slot | Marks internal group parts (root, header, label, description, items) and each control (item, box, fill, glass, overlay, indicator, indicator-icon, wrapper, item-label, item-description). |
The component reuses the shared glass material and the Checkbox item styling. Tune shared --ui-glass-* tokens on an ancestor, adjust group spacing with --ui-checkbox-group-gap and --ui-checkbox-group-item-gap, and use ui for internal slots keyed by data-slot. ui.item styles every option root; per-entry ui targets the same control slots. Component-specific values live in src/theme/checkbox-group.ts. See the theming guide for token layers and presets.