Checkbox
Binary or tri-state selection control built on Radix Checkbox, with indeterminate support and form-payload semantics.
Overview
Checkbox is built on @radix-ui/react-checkbox. It supports the three canonical states - unchecked, checked, and indeterminate - via Radix's checked prop, which accepts true | false | "indeterminate". The indicator renders a CheckIcon from lucide.
Use Checkbox when the value is part of a form submission (terms acceptance, multi-select filters, table row selection). Reach for Switch instead when the change takes effect immediately, without submission (notifications on / off, dark mode).
Preview
Installation
bash npx gremorie@latest add rx-checkbox bash pnpm dlx gremorie@latest add rx-checkbox bash yarn dlx gremorie@latest add rx-checkbox bash bunx --bun gremorie@latest add rx-checkbox Usage
import { Checkbox, Label } from "@gremorie/rx-forms";
export function Example() {
return (
<div className="flex items-center gap-2">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
);
}Angular edition planned for Phase 5h. Star the repo to track progress.
API
<Checkbox>
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | "indeterminate" | - | Controlled checked state. Pass "indeterminate" for the dash indicator. |
defaultChecked | boolean | "indeterminate" | - | Uncontrolled initial state. |
onCheckedChange | (checked: boolean | "indeterminate") => void | - | Fires when the user toggles the checkbox. |
disabled | boolean | false | Disables interaction. |
required | boolean | false | Mark as required for form submission. |
name | string | - | Form field name. |
value | string | "on" | Form field value when checked. |
aria-invalid | boolean | - | Switches border / ring to destructive token. |
Forwards all props to CheckboxPrimitive.Root and renders a CheckboxPrimitive.Indicator with a CheckIcon inside. The data-state attribute (unchecked, checked, indeterminate) drives the visual surface.
Composition
- Always pair with a
<Label>viahtmlFormatching the checkbox'sid. The label is the affordance most users click. - Inside a
<Form>, use<FormField>with<FormControl>so ARIA wiring happens automatically. - For "select all" patterns, drive the master checkbox into
indeterminatewhen some (but not all) children are checked.
Variations
Terms acceptance
The canonical form pattern. Checkbox + Label side by side.
<div className="flex items-center gap-2">
<Checkbox id="terms" />
<Label htmlFor="terms">
I accept the{' '}
<a href="/terms" className="underline">
terms
</a>
</Label>
</div>Default checked
Use for opt-in defaults like newsletter signup. Always make the default state explicit to users.
<div className="flex items-center gap-2">
<Checkbox id="newsletter" defaultChecked />
<Label htmlFor="newsletter">Subscribe to the changelog</Label>
</div>Indeterminate "select all"
Master checkbox flips to indeterminate when some children are selected. Clicking it should clear all when indeterminate / checked, and select all when unchecked.
function SelectAll({ items, selected, onChange }) {
const allSelected = selected.length === items.length;
const someSelected = selected.length > 0 && !allSelected;
return (
<div className="flex items-center gap-2">
<Checkbox
id="select-all"
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
onCheckedChange={(value) => {
onChange(value === true ? items.map((i) => i.id) : []);
}}
/>
<Label htmlFor="select-all">Select all</Label>
</div>
);
}Disabled with label
Label reads peer-disabled and fades automatically when the sibling peer-marked checkbox is disabled. Radix's Checkbox.Root includes the peer class.
<div className="flex items-center gap-2">
<Checkbox id="legacy" disabled />
<Label htmlFor="legacy">Migrate from legacy account (coming soon)</Label>
</div>Accessibility
- Native semantics: Radix renders
role="checkbox"witharia-checkedreflecting the state (true,false,mixedfor indeterminate). - Keyboard:
Spacetoggles the checkbox;Tab/Shift+Tabmove focus. - Label association: clicking the
<Label>toggles the checkbox via the standardhtmlFormechanism. - Indeterminate:
aria-checked="mixed"is announced as "mixed" by screen readers, signalling partial selection. - Required + invalid: combine
requiredwitharia-invalid="true"and an error message; the destructive ring kicks in automatically.
Related
- Switch - immediate-effect toggle (vs form-submission selection)
- Radio Group - single-select cousin
- Label - the canonical companion
- Form - wire Checkbox into react-hook-form