Checkbox
Independent choices, confirmations, and mixed selection.
Preview
Use Checkbox for an independent yes/no choice or multiple selections from a list. Clicking the label toggles its control.
Show archived projects alongside active work.
Showing active projects only. Local preview only.
Installation
pnpm dlx shadcn@latest add http://127.0.0.1:3017/r/checkbox.jsonInstalls Checkbox, its Radix primitive, and Lucide for the check and minus indicators. It preserves your theme. Field, Label, and Button are separate optional items. See the registry guide for local endpoints and updates.
Usage
import { Checkbox } from '@/components/ui/checkbox';
import { Field, FieldLabel } from '@/components/ui/field';
<Field orientation="horizontal" className="gap-2">
<Checkbox id="updates" name="updates" value="email" />
<FieldLabel htmlFor="updates">Email project updates</FieldLabel>
</Field>The mark is 16px square with a 2px radius and a 14px indicator. A visible label expands the clickable region. Allow at least 8px between control and label. Descriptions and errors use the 12px / 16px caption role. FieldContent places the label and description together without added gap; other field parts retain 4px spacing. No shadow is applied.
Mixed selection
The parent checkbox is mixed when some children are checked. Toggle it to select or clear all. The application computes this state; Checkbox does not discover child selections automatically.
<Checkbox
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
onCheckedChange={checked => setAllSelected(checked === true)}
aria-label="Select all channels"
/>Keep checked as boolean | 'indeterminate' when your state can be mixed. Do not convert 'indeterminate' to true just because it is truthy. Use FieldSet and FieldLegend to name related choices.
Interaction states
These are isolated state specimens. Hover and focus are pinned using the real interaction utilities; enabled examples remain interactive. Checked and mixed states use the primary fill and its matching foreground.
Invalid controls use a destructive border and focus outline; labels remain neutral. Disabled controls are dimmed and skipped by Tab. Set data-disabled on Field to dim the label too. Pair real invalid controls with a visible error message.
Required confirmation and form values
Submit without checking to see the error and focus move to the checkbox. Check it, submit again, then reset. This is local validation; nothing is saved.
Use required on the control and aria-invalid when validation fails. Link errors with aria-describedby. The demo uses noValidate for custom feedback. For real submissions, validate on the server too.
Inside a form, Radix supplies the form input. A checked checkbox contributes its name and value (default "on"); unchecked, mixed, and disabled controls do not contribute a checked value. For controlled components, reset the parent state explicitly when resetting a form.
Keyboard and accessibility
- Tab focuses an enabled checkbox; Space toggles it. Preserve Radix's keyboard behavior.
- Associate a visible label through
htmlForandid; do not place links or buttons inside the clickable label. - Link helper and error text with
aria-describedby. - The mixed state is announced as mixed, not as checked.
- Checkbox has no read-only mode. Use disabled only when the choice is unavailable, and explain the reason where needed.
- For touch, provide a generous label/row target; a 16px mark is not a 44px touch target by itself.
API
| Prop | Default | Purpose |
|---|---|---|
checked / defaultChecked | Unchecked | Controlled or initial boolean / 'indeterminate' state |
onCheckedChange | — | Receives boolean or 'indeterminate' |
disabled | false | Disable the control |
required | false | Require a checked value in a form |
name / value | Value "on" | Form submission fields |
aria-invalid | Unset | Apply invalid border and focus treatment |
| Radix Root props | — | Includes ref, id, and accessibility attributes |
Use Radio Group for one choice from a set and Switch for a setting that applies immediately.