gray
radius
Forms

Checkbox

Any number of a set. Square, because that shape is what tells a user the choices are not exclusive. They read the shape before the label.

Indeterminate

Pass checked="indeterminate" for the mixed state. Toggle the children below to see it.

<Checkbox
checked={all ? true : some ? 'indeterminate' : false}
onCheckedChange={() => setChecked(all ? [] : CHILDREN)}
>
Regenerate all scales
</Checkbox>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, paddingLeft: 28 }}>
{CHILDREN.map(name => (
<Checkbox
key={name}
checked={checked.includes(name)}
onCheckedChange={value =>
setChecked(current =>
value ? [...current, name] : current.filter(n => n !== name)
)
}
>
{name}
</Checkbox>
))}
</div>
indeterminate is a real third state, not a visual trick. The base layer reports it as aria-checked="mixed", which tells a screen reader user that toggling it affects several things at once.

State

On, off, and either of those with disabled.

<Checkbox defaultChecked>Run tests before publishing</Checkbox>
<Checkbox>Include prerelease tags</Checkbox>
<Checkbox disabled>Sign with GPG</Checkbox>
<Checkbox disabled defaultChecked>Publish provenance</Checkbox>

Label

Children give the box a label and the association between them. Omit them for the box alone, when something else does the labelling.

<Checkbox defaultChecked>Run tests before publishing</Checkbox>
<Checkbox defaultChecked />

Props

PropTypeDefaultNotes
checkedboolean | 'indeterminate'Controlled state. Indeterminate announces as mixed.
defaultCheckedbooleanUncontrolled initial state.
onCheckedChange(checked: boolean | 'indeterminate') => voidFires on every tick. Clicking a mixed box emits true, never 'indeterminate'.
childrenReactNodeLabel text. Omit to render the box alone.
disabledbooleanfalseBlocks the tick and dims the box, in any state.