Checkbox Group
Shared state for a series of Checkboxes. Renders a
<div role="group"> and owns the list of ticked values, so the answer to a
multiple-choice question is one array instead of one boolean per option.
Unlike a RadioGroup or a
ToggleGroup, this is not a keyboard mode.
Every checkbox in the group stays its own tab stop, exactly as a run of native
checkboxes would — grouping here is about state and about naming, and the
arrow keys are left to the page.
Import
import { Checkbox, CheckboxGroup } from "@forte-ui/react";Give each Checkbox a value; the group's value is the list of ticked ones.
A checkbox without a value can never appear in it, and stays a plain
standalone checkbox holding its own state.
Examples
Labelling
The group has no name of its own, and it cannot be given one with a <label> —
a <label> resolves to a single control, and role="group" is not a labelable
element. Point aria-labelledby at a heading you render yourself, and name each
checkbox with an enclosing <label>.
The enclosing label works because Checkbox renders a <span> with a hidden
<input> beside it, and the input is what the label resolves to. Inside a
Field.Root — the pattern the rest of this page uses — a
Field.Label nativeLabel={false} does the heading half for you, and a
Field.Item per row does the other half.
Rendering as a native button
Pass nativeButton render={<button />} when you want a real <button> instead
of the default <span>. That also moves id from the hidden input onto the
root, which is what makes a sibling <label htmlFor> resolve. For a button
inside an enclosing label, use the render callback instead.
Orientation
orientation="horizontal" lays the checkboxes out in a row instead of a
column, and wraps when they run out of room.
Controlled
Pass value and onValueChange for a controlled group, defaultValue for an
uncontrolled one. The callback receives the new array first and Base UI's event
details second.
2 of 4 selected.
Holding the array yourself is also what lets something outside the group write to it — a "select all" button, a URL parameter, a saved preset.
Parent checkbox
Add parent to a checkbox and the group drives it: ticked when every child is
ticked, mixed when only some are, unticked when none are. The mixed state draws
a dash instead of a tick and reports aria-checked="mixed".
Three details worth knowing about a parent checkbox:
- Activating it while it is mixed ticks every child. Activating it again unticks them all. A third activation restores the partial selection it started from.
- It is left out of form submission: Base UI drops the
nameon a parent checkbox, so only the children submit values. - Its
aria-controlsis built from ids the group generates for the children, so do not pass your ownidto a child checkbox inside a parent group — it breaks the association.
Nested parent checkboxes
A group can contain another group, each with its own parent. Nothing connects the two automatically, though: a row that is both a value in the outer group and the parent of the inner one is two facts about two different groups, and you keep them in step yourself.
Two pieces of that are easy to miss. The outer parent cannot see the level
below, so a partly-filled inner group has to be reported upwards with an
explicit indeterminate — otherwise the top checkbox reads as empty while four
sub-permissions are granted. And unticking the middle row should only clear the
inner group when it was completely full, since that is the state the row was
standing for; clearing a partial selection throws away choices the user made one
level down.
Form integration
Inside a Form, a Field.Root carries the name the array is submitted under.
Render the fieldset as the group so that one element is both the
<fieldset> Base UI names with its legend and the group that owns the value.
One thing that composition costs you: Fieldset.Root normally renders a real
<fieldset>, and disabled on it works the native way — it reaches controls
the library has never heard of. Rendered as the group it is a <div>, so put
disabled on the CheckboxGroup or the Field.Root instead.
Disabled
disabled on the group disables every checkbox in it. Put it on the
surrounding Field.Root instead and the labels dim with the boxes, which is
what keeps the reason an option is unavailable from sitting at full contrast
beside a greyed-out control. One row at a time is disabled on that row's
Field.Item, for exactly the same reason.
Replication is fixed for the duration of a migration.
Accessibility
| Key | Behaviour |
|---|---|
| Tab | Moves focus to the next checkbox. Every checkbox in the group is its own tab stop — the group is not a roving-focus widget. |
| Space | Toggles the focused checkbox. On a parent checkbox it drives the whole group — all, none, then back to the earlier partial selection. |
| Enter | Does not toggle. Inside a form it submits the form, matching a native checkbox. |
Each checkbox still needs its own name too — an enclosing <label>, a
Field.Item with a Field.Label, or a sibling <label htmlFor> paired with
nativeButton. See Checkbox for the rest: the mark,
the mixed state, forced colours and motion all belong to the individual control.
Theming
The group declares one property of its own; the checkboxes inside are themed
through the --forte-checkbox-* properties documented on
Checkbox.
| Property | Controls | Default |
|---|---|---|
--forte-checkbox-group-gap | Gap between items in the group. var(--forte-space-4) when orientation="horizontal". | var(--forte-space-2) |
API reference
CheckboxGroup
| Prop | Type | Default | Description |
|---|---|---|---|
allValues | string[] | undefined | Values of *all* checkboxes in the group, ticked or not. Needed only for a parent checkbox: it is how the group knows the difference between "some" and "all" and therefore when the parent is indeterminate. |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
defaultValue | string[] | undefined | Values of the checkboxes that start ticked when uncontrolled. |
disabled | boolean | false | Disables every checkbox in the group. |
orientation | CheckboxGroupOrientation | vertical | Direction the checkboxes are laid out in. Horizontal groups wrap. |
value | string[] | undefined | Values of the checkboxes that are ticked. Pairs with `onValueChange` for a controlled group; use `defaultValue` for an uncontrolled one. Required if any child uses the `parent` prop. |
onValueChange, render and the rest of Base UI's CheckboxGroup props pass
through unchanged.
Checkbox
See Checkbox for the full prop table.