Checkbox is a native <input type="checkbox"> with a label beside it and a
painted box standing in for the one the browser draws. One checkbox answers one
question. It is also the only control in PDS that can report a group as partly
answered.
Which component you render depends on how many boxes there are: CheckboxField
for one, CheckboxGroupField for a set. The difference is not cosmetic, and the
section below says what goes wrong when a set is composed by hand.
Anatomy
Section titled “Anatomy”-
Container holds the box and its label together, and is the click target for both. -
Box the square that carries the answer. One box, three states: empty, checked, and the dash that means indeterminate. -
Label the option, in words. It is the checkbox’s accessible name, so it is not optional.
The painted box is aria-hidden and the real input sits on top of it,
transparent. State arrives as a shape before it arrives as a color: a check for
checked, a dash for indeterminate, an empty box for neither, so the three
states differ in more than their fill.
When to use
Section titled “When to use”Nothing happens when a checkbox is clicked. It is a real form input, carrying a
value and emitting a change event and posting with the form it sits in, so the
answer is recorded now and takes effect on submit. That is what suits it to a
question rather than to a switch: there is no confirmation step to design, and
no feedback owed beyond the check appearing.
Radio is the control where only one of the options may be chosen at a time, a Toggle where the change should apply the moment it is switched, and a Combobox where the list is long enough that the reader would rather search it than read it.
CheckboxField has two label props, and you want one of them
Section titled “CheckboxField has two label props, and you want one of them”CheckboxField is a checkbox with a FormField around
it, and it splits the label in two: label renders above the box, which is
where a question goes, and checkboxLabel renders beside it, which is where an
option goes. A single checkbox usually wants one or the other.
Pass both and the checkbox gets two <label> elements pointing at the same
input, because the checkbox takes its id from the field’s context and its own
label uses that same id. A screen reader then reads the question and the option
as one run-on name. Use label when the text is a question the box answers, and
checkboxLabel when it is the thing being agreed to.
Six of the twelve field props do not reach the field. CheckboxField forwards
label, checkboxLabel, helperText, warningText, errorText,
configField and labelProps, and spreads the rest onto the <input>, so
inputId, rightActions, configFieldInputWidth, helperProps,
warningProps and errorProps are accepted and then dropped. They appear in
the props table because they are in the type.
Three checkboxes in one FormField share one id
Section titled “Three checkboxes in one FormField share one id”A FormField hands one input id to everything inside it, and that id wins over a
checkbox’s own. So three checkboxes hand-composed into one FormField render
three identical ids, every label’s for attribute points at the first box, and
clicking the second label toggles the first. A screen reader announces all three
with the first one’s label.
CheckboxGroupField exists for this. It clears the id before rendering its
children, so each box generates its own, and it wires the group on top of that:
React Aria’s checkbox group gives the set a role="group", holds the selection
as an array of strings in value and onChange, and derives each child’s
checked from it. disabled, readOnly and required on the group reach every
box in it.
It is also the one composed field whose error message is announced. React Aria
generates the ids for the helper and error text and lists both in the group’s
aria-describedby, which is the association FormField
otherwise leaves to you.
The children have to be checkbox elements. The group clones each one to inject its state, so a checkbox wrapped in a layout div is a child the group cannot reach. That is also the case where a hand-composed FormField is still right: a field holding a checkbox and something else, where the ids are yours to keep distinct.
One radio or toggle in a column of checkboxes changes what the column means
Section titled “One radio or toggle in a column of checkboxes changes what the column means”A column of checkboxes tells the reader the answers are independent, and one radio or toggle dropped into it withdraws that promise without saying so. If exactly one option may be chosen, the whole group is Radio instead.
States
Section titled “States”Reserve indeterminate for the parent of a group
Section titled “Reserve indeterminate for the parent of a group”indeterminate shows a dash rather than a check and sets
aria-checked="mixed", which is the state of a parent whose children are only
partly selected. No other PDS control can say it: Toggle’s value is a boolean,
and a radio group’s state is which member, not how many.
The dash is display only. It is written onto the DOM node in an effect and never
touches checked, so a parent rendered indeterminate still submits and reports
whatever checked says. Derive the parent’s checked from its children and use
indeterminate for the third case; do not let the dash stand in for a value.
Choose controlled or uncontrolled on the first render
Section titled “Choose controlled or uncontrolled on the first render”Pass checked and the checkbox is yours to update. Omit it, pass
defaultChecked, and the checkbox keeps its own state. Which mode it is in is
latched the first time it renders, so a checkbox mounted with checked still
undefined while its data loads stays uncontrolled for its whole life, and
later passing a real boolean will not move it. Render it once the value exists,
or give it a key that changes with the value.
Split the option and its elaboration between label and tooltip
Section titled “Split the option and its elaboration between label and tooltip”tooltip wraps the label in a tooltip and underlines it with a dashed rule, so
the label advertises that there is more to read. It hangs off the label element
rather than the input, which makes it an aside rather than part of what the
checkbox announces. Put the option in the label and the elaboration in the
tooltip, never the other way round.
Accessibility
Section titled “Accessibility”A checkbox is named by its label, or by a heading you point at
Section titled “A checkbox is named by its label, or by a heading you point at”label renders a real <label> pointed at the input, using the id you passed
or a generated one. Inside Form Field the field’s own
input id wins over the id prop, so the checkbox adopts the field’s label
instead of carrying its own. CheckboxGroupField is the one wrapper that gives
the id back, which is why a set belongs in it.
Where the visible label lives somewhere else entirely, a column header above a
column of checkboxes, point aria-labelledby at it. A checkbox with neither a
label nor an aria-labelledby reaches a screen reader as a checkbox with no
name, and the row it sits in is not read out with it.
How to word the label itself is on Writing.
Three components, three tables. CheckboxField is a checkbox’s props plus the
twelve FormField carries, plus checkboxLabel:
| Name | Type |
|---|---|
checkboxLabel |
|
label |
Renders |
inputId |
ID of the input element. Providing this will automatically link the label to the input. |
helperText |
Renders |
errorText |
Renders |
warningText |
Renders |
labelProps |
|
helperProps |
|
warningProps |
|
errorProps |
|
configField |
Whether to render the form field as a config field. This will render the helper text as a tooltip. |
configFieldInputWidth |
Override the width of the input area when |
rightActions |
Renders actions to the right of the label. |
id |
|
onChange |
Called whenever the checked state changes. |
disabled |
Disables interaction and renders the checkbox in a muted style. |
indeterminate |
When true the checkbox displays a dash (−) instead of a tick, indicating a mixed / partially-selected state. Sets |
checked |
Controlled checked state. Omit (or pass |
value |
Value submitted with a form. |
data-test |
|
containerProps |
Additional props forwarded to the outer container |
size |
Size of the checkbox and its label text. |
tooltip |
Tooltip content to display when hovering over the label. When provided, the label will be wrapped in a tooltip. |
Omit<ComponentPropsWithoutRef<‘input’>, ‘value’ | ‘size’>.CheckboxGroupField is the shortest of the three, because most of what it does
comes from React Aria’s checkbox group rather than from props of its own —
value, onChange and the rest arrive through the base type at the bottom of
the table. It has no configField:
| Name | Type |
|---|---|
children |
|
disabled |
|
readOnly |
|
required |
|
label |
Renders |
inputId |
ID of the input element. Providing this will automatically link the label to the input. |
helperText |
Renders |
errorText |
Renders |
warningText |
Renders |
labelProps |
|
helperProps |
|
warningProps |
|
errorProps |
|
configFieldInputWidth |
Override the width of the input area when |
rightActions |
Renders actions to the right of the label. |
Omit< AriaCheckboxGroupProps, ‘description’ | ‘disabled’ | ‘isReadOnly’ | ‘isRequired’ | ‘errorMessage’ >.Checkbox’s own props, which CheckboxField also accepts and
CheckboxGroupField expects on each child:
| Name | Type |
|---|---|
id |
|
onChange |
Called whenever the checked state changes. |
label |
Visible label rendered to the right of the checkbox box. |
disabled |
Disables interaction and renders the checkbox in a muted style. |
indeterminate |
When true the checkbox displays a dash (−) instead of a tick, indicating a mixed / partially-selected state. Sets |
checked |
Controlled checked state. Omit (or pass |
value |
Value submitted with a form. |
data-test |
|
containerProps |
Additional props forwarded to the outer container |
size |
Size of the checkbox and its label text. |
tooltip |
Tooltip content to display when hovering over the label. When provided, the label will be wrapped in a tooltip. |
Omit<ComponentPropsWithoutRef<‘input’>, ‘value’ | ‘size’>.Was this page helpful?