Checkbox
Checkboxes allow users to select one or more items from a set of options or to toggle a single setting on or off.
Examples
Basic
States
Disabled
Custom indicator
Hit area
Controlled
Overview
Checkbox provides a standard form control for binary or tri-state selections. It supports checked, unchecked, and indeterminate states, with built-in accessibility and keyboard navigation.
import { Checkbox } from "@rogo-technologies/ui/checkbox";
<div className="flex items-center gap-1">
<Checkbox id="newsletter" />
<label htmlFor="newsletter">Subscribe to newsletter</label>
</div>;Usage
States
checked accepts true, false, and "mixed" (or "indeterminate").
| State | Value | Use case |
|---|---|---|
| Unchecked | false | Option is not selected |
| Checked | true | Option is selected |
| Mixed | "mixed" or "indeterminate" | Parent checkbox when some children are selected |
<Checkbox id="option" /> // uncontrolled
<Checkbox checked={false} />
<Checkbox checked={true} />
<Checkbox checked="mixed" />
<Checkbox checked="indeterminate" /> // aliasControlled
const [isChecked, setIsChecked] = useState(false);
<Checkbox checked={isChecked} onCheckedChange={(checked) => setIsChecked(checked)} />;Disabled
<Checkbox disabled />
<Checkbox checked disabled />
<div className="flex items-center gap-1">
<Checkbox id="disabled" disabled />
<label htmlFor="disabled" className="opacity-50">
Unavailable option
</label>
</div>Groups with "select all"
Combine multiple checkboxes; use mixed state on the parent when some children are selected.
const [selectedItems, setSelectedItems] = useState<string[]>([]);
const allItems = ["Option 1", "Option 2", "Option 3"];
const allSelected = selectedItems.length === allItems.length;
const someSelected = selectedItems.length > 0 && !allSelected;
<Checkbox
checked={allSelected ? true : someSelected ? "mixed" : false}
onCheckedChange={(checked) => {
setSelectedItems(checked ? allItems : []);
}}
/>
<label>Select all</label>
{allItems.map((item) => (
<Checkbox
key={item}
checked={selectedItems.includes(item)}
onCheckedChange={(checked) => {
setSelectedItems((prev) =>
checked ? [...prev, item] : prev.filter((i) => i !== item)
);
}}
/>
))}Hit area
The visual checkbox is 16px, below the 24px minimum pointer target. hitArea renders an invisible ghost element around the control that enlarges its clickable area without affecting layout. Clicks on the ghost activate the checkbox natively: no wrapper element, no extra click handler.
| Size | Extra reach per edge | Resulting target |
|---|---|---|
sm | 4px | 24px |
md | 8px | 32px |
lg | 12px | 40px |
<Checkbox hitArea="md" />The ghost is a before: pseudo-element, so its reach can be retuned per edge when needed (e.g. className="before:-inset-y-3"). It can overlap adjacent elements and wins pointer events over earlier-painted siblings, so leave breathing room around dense control clusters.
The same mechanism is available to any element via hitAreaVariants from @rogo-technologies/ui/hit-area (Button supports the same hitArea prop).
Composable API
Use CheckboxRoot and CheckboxIndicator to provide custom indicator content.
import { CheckboxRoot, CheckboxIndicator } from "@rogo-technologies/ui/checkbox";
<CheckboxRoot id="option">
<CheckboxIndicator />
</CheckboxRoot>
<CheckboxRoot id="option">
<CheckboxIndicator>
<span className="text-xs">✓</span>
</CheckboxIndicator>
</CheckboxRoot>
<CheckboxRoot id="option">
<CheckboxIndicator>
<IconStar className="size-3" />
</CheckboxIndicator>
</CheckboxRoot>With form libraries
import { useForm } from "react-hook-form";
const { register, handleSubmit } = useForm();
<form onSubmit={handleSubmit(onSubmit)}>
<Checkbox {...register("acceptTerms")} />
<label>Accept terms and conditions</label>
</form>;Accessibility
- Keyboard:
Spacetoggles - Focus ring is visible
- Associates with labels via
idandhtmlFor - Supports
aria-describedby - Mixed state is announced to screen readers
<Checkbox id="option" aria-describedby="option-description" />
<label htmlFor="option">Enable notifications</label>
<p id="option-description">You'll receive email alerts for new messages.</p>API
Checkbox
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | "mixed" | "indeterminate" | — | Controlled checked state. |
defaultChecked | boolean | false | Initial checked state for uncontrolled usage. |
onCheckedChange | (checked: boolean) => void | — | Callback when checked state changes. |
disabled | boolean | false | Disables the checkbox. |
required | boolean | false | Marks the checkbox as required. |
name | string | — | Name attribute for form submission. |
value | string | "on" | Value attribute for form submission. |
hitArea | "sm" | "md" | "lg" | — | Enlarges the pointer target by 4/8/12px per edge via an invisible ghost element. |
CheckboxRoot
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | — | Controlled checked state. |
defaultChecked | boolean | false | Initial checked state. |
onCheckedChange | (checked: boolean) => void | — | Callback when checked state changes. |
disabled | boolean | false | Disables the checkbox. |
hitArea | "sm" | "md" | "lg" | — | Enlarges the pointer target by 4/8/12px per edge via an invisible ghost element. |
CheckboxIndicator
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | Checkmark icon | Custom indicator content. |
Guidelines
Do
- Always pair checkboxes with visible labels
- Use the mixed state for "select all" checkboxes when some items are selected
- Group related checkboxes together visually
- Use checkboxes for independent options that can all be selected
- Provide clear, concise labels that describe what selecting the checkbox does
- Give standalone checkboxes a generous pointer target with
hitAreawhen they aren't inside a larger clickable row or label
Don't
- Don't use checkboxes for mutually exclusive options — use radio buttons instead
- Don't use a checkbox for a single binary action — consider a switch/toggle instead
- Don't disable checkboxes without explanation — provide context via tooltip or helper text
- Don't use negative labels ("Don't send emails") — use positive phrasing ("Send emails")
- Don't nest checkboxes more than one level deep for "select all" patterns