Components
Checkbox
A styled checkbox with label, description, and mixed-state support. Built on Radix Checkbox, so the visible control is a button that reports role="checkbox".
Basic usage
With description
Add a description for more context.
Receive emails about new features and updates.
Indeterminate
Use indeterminate for a "select all" pattern where only some children are checked. State changes arrive through onCheckedChange, not onChange.
Mixed state
The Radix state value "indeterminate" can be passed straight to checked or defaultChecked. The dash and the tick are both rendered and switched by the indicator's own data-state, so the icon always matches the real state — including the uncontrolled case, where the box keeps tracking the user's clicks after it starts out mixed. The indeterminate prop stays as a shorthand for the same thing.
Click it: the dash becomes a tick because the icon reads data-state, not the prop.
Current state: indeterminate
Disabled
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Text label rendered beside the checkbox. Passing label or description wraps the control in a layout row. |
description | string | — | Secondary helper text below the label. |
indeterminate | boolean | — | Back-compat shortcut for the mixed state. When true it overrides checked with "indeterminate". |
checked | boolean | 'indeterminate' | — | Controlled state. Pair with onCheckedChange. |
defaultChecked | boolean | 'indeterminate' | — | Initial state when uncontrolled — "indeterminate" is accepted here too. |
onCheckedChange | (checked: boolean | 'indeterminate') => void | — | Fires when the state changes. There is no onChange — the control is a button, not an input. |
disabled | boolean | false | Blocks interaction and drops opacity to 50%. |
required | boolean | false | Marks the checkbox as required inside a native form. |
name | string | — | Name submitted with the hidden form input when rendered inside a form. |
value | string | 'on' | Value submitted with the form when checked. |
id | string | auto | Element id. Generated with useId when omitted, so the built-in label still binds correctly. |
className | string | — | Additional Tailwind classes merged onto the control. |
...rest | Radix Checkbox.Root props | — | Everything else is forwarded to the underlying button. |
Accessibility
- Radix renders a button with
role="checkbox"; the mixed state is announced asaria-checked="mixed". - Inside a
<form>a hidden input is mirrored alongside the button, sonameandvaluesubmit natively. - Label is clickable and associated via
htmlFor; when noidis passed the component generates one withuseId. - Colour goes through the shared control tokens rather than the raw gold scale: the checked box is
bg-control-on, the tick istext-control-check, and the resting border isborder-choice-edge. That is what lets the high-contrast palette re-tune the control: it darkens--control-onfrom #E8B824 to #B48700 while the tick stays--inkin both palettes — 10.19:1 on the default gold, 5.77:1 on the high-contrast one. The token that does swap to the page background under high contrast is--control-knob, the Switch knob and Radio dot. - Hover deepens the border to
border-choice-edge-strong, never lightens it — the resting border already sits at 2.99:1 against the page on the default palette (3.83:1 on high contrast).