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

PropTypeDefaultDescription
labelstringText label rendered beside the checkbox. Passing label or description wraps the control in a layout row.
descriptionstringSecondary helper text below the label.
indeterminatebooleanBack-compat shortcut for the mixed state. When true it overrides checked with "indeterminate".
checkedboolean | 'indeterminate'Controlled state. Pair with onCheckedChange.
defaultCheckedboolean | 'indeterminate'Initial state when uncontrolled — "indeterminate" is accepted here too.
onCheckedChange(checked: boolean | 'indeterminate') => voidFires when the state changes. There is no onChange — the control is a button, not an input.
disabledbooleanfalseBlocks interaction and drops opacity to 50%.
requiredbooleanfalseMarks the checkbox as required inside a native form.
namestringName submitted with the hidden form input when rendered inside a form.
valuestring'on'Value submitted with the form when checked.
idstringautoElement id. Generated with useId when omitted, so the built-in label still binds correctly.
classNamestringAdditional Tailwind classes merged onto the control.
...restRadix Checkbox.Root propsEverything else is forwarded to the underlying button.

Accessibility

  • Radix renders a button with role="checkbox"; the mixed state is announced as aria-checked="mixed".
  • Inside a <form> a hidden input is mirrored alongside the button, so name and value submit natively.
  • Label is clickable and associated via htmlFor; when no id is passed the component generates one with useId.
  • Colour goes through the shared control tokens rather than the raw gold scale: the checked box is bg-control-on, the tick is text-control-check, and the resting border is border-choice-edge. That is what lets the high-contrast palette re-tune the control: it darkens --control-on from #E8B824 to #B48700 while the tick stays --ink in 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).