Checkbox
Binary control with indeterminate state. Controlled or uncontrolled via onCheckedChange.
Examples
Basic
Pair the checkbox with Field and FieldLabel for proper layout and labeling.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
export function CheckboxBasic() {
return (
<FieldGroup className="mx-auto w-56">
<Field orientation="horizontal">
<Checkbox id="terms-checkbox-basic" name="terms-checkbox-basic" />
<FieldLabel htmlFor="terms-checkbox-basic">Accept terms and conditions</FieldLabel>
</Field>
</FieldGroup>
);
}
Description
Use FieldContent and FieldDescription for helper text.
By clicking this checkbox, you agree to the terms and conditions.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel } from "@codefast/ui/field";
export function CheckboxDescription() {
return (
<FieldGroup className="mx-auto w-72">
<Field orientation="horizontal">
<Checkbox id="terms-checkbox-desc" name="terms-checkbox-desc" defaultChecked />
<FieldContent>
<FieldLabel htmlFor="terms-checkbox-desc">Accept terms and conditions</FieldLabel>
<FieldDescription>By clicking this checkbox, you agree to the terms and conditions.</FieldDescription>
</FieldContent>
</Field>
</FieldGroup>
);
}
Disabled
Use the disabled prop to prevent interaction and add the data-disabled attribute to the <Field> component for disabled styles.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
export function CheckboxDisabled() {
return (
<FieldGroup className="mx-auto w-56">
<Field orientation="horizontal" data-disabled>
<Checkbox id="toggle-checkbox-disabled" name="toggle-checkbox-disabled" disabled />
<FieldLabel htmlFor="toggle-checkbox-disabled">Enable notifications</FieldLabel>
</Field>
</FieldGroup>
);
}
Group
Group related checkboxes inside a FieldSet with a FieldLegend and shared description.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldDescription, FieldGroup, FieldLabel, FieldLegend, FieldSet } from "@codefast/ui/field";
export function CheckboxGroup() {
return (
<FieldSet>
<FieldLegend variant="label">Show these items on the desktop:</FieldLegend>
<FieldDescription>Select the items you want to show on the desktop.</FieldDescription>
<FieldGroup className="gap-3">
<Field orientation="horizontal">
<Checkbox
id="finder-pref-9k2-hard-disks-ljj-checkbox"
name="finder-pref-9k2-hard-disks-ljj-checkbox"
defaultChecked
/>
<FieldLabel htmlFor="finder-pref-9k2-hard-disks-ljj-checkbox" className="font-normal">
Hard disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox
id="finder-pref-9k2-external-disks-1yg-checkbox"
name="finder-pref-9k2-external-disks-1yg-checkbox"
defaultChecked
/>
<FieldLabel htmlFor="finder-pref-9k2-external-disks-1yg-checkbox" className="font-normal">
External disks
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox id="finder-pref-9k2-cds-dvds-fzt-checkbox" name="finder-pref-9k2-cds-dvds-fzt-checkbox" />
<FieldLabel htmlFor="finder-pref-9k2-cds-dvds-fzt-checkbox" className="font-normal">
CDs, DVDs, and iPods
</FieldLabel>
</Field>
<Field orientation="horizontal">
<Checkbox
id="finder-pref-9k2-connected-servers-6l2-checkbox"
name="finder-pref-9k2-connected-servers-6l2-checkbox"
/>
<FieldLabel htmlFor="finder-pref-9k2-connected-servers-6l2-checkbox" className="font-normal">
Connected servers
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
);
}
Indeterminate
Pass checked="indeterminate" to a parent that controls a list when only some children are selected.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { useState } from "react";
const permissions = [
{ id: "read", label: "Read", hint: "View content and settings" },
{ id: "write", label: "Write", hint: "Create and edit content" },
{ id: "manage", label: "Manage", hint: "Invite members and change roles" },
];
export function CheckboxIndeterminate() {
const [checkedIds, setCheckedIds] = useState<Set<string>>(new Set(["read"]));
const allChecked = checkedIds.size === permissions.length;
const someChecked = checkedIds.size > 0 && !allChecked;
const parentState = someChecked ? "indeterminate" : allChecked;
const handleParentChange = (checked: boolean | "indeterminate") => {
setCheckedIds(checked === true ? new Set(permissions.map((permission) => permission.id)) : new Set());
};
const handleChildChange = (id: string, checked: boolean | "indeterminate") => {
setCheckedIds((previous) => {
const next = new Set(previous);
if (checked === true) {
next.add(id);
} else {
next.delete(id);
}
return next;
});
};
return (
<div className="mx-auto w-full max-w-xs rounded-xl border p-4">
<FieldGroup>
<Field orientation="horizontal">
<Checkbox
aria-label="Grant all permissions"
checked={parentState}
id="permissions-all"
onCheckedChange={handleParentChange}
/>
<FieldLabel htmlFor="permissions-all">All permissions</FieldLabel>
</Field>
<FieldGroup className="ms-6 gap-4">
{permissions.map((permission) => (
<Field key={permission.id} orientation="horizontal">
<Checkbox
checked={checkedIds.has(permission.id)}
id={`permission-${permission.id}`}
onCheckedChange={(checked) => {
handleChildChange(permission.id, checked);
}}
/>
<FieldLabel htmlFor={`permission-${permission.id}`} className="font-normal">
{permission.label}
<span className="block text-xs font-normal text-ui-muted">{permission.hint}</span>
</FieldLabel>
</Field>
))}
</FieldGroup>
</FieldGroup>
</div>
);
}
Invalid State
Use aria-invalid to style the checkbox in an invalid state.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
export function CheckboxInvalid() {
return (
<FieldGroup className="mx-auto w-56">
<Field orientation="horizontal" data-invalid>
<Checkbox id="terms-checkbox-invalid" name="terms-checkbox-invalid" aria-invalid />
<FieldLabel htmlFor="terms-checkbox-invalid">Accept terms and conditions</FieldLabel>
</Field>
</FieldGroup>
);
}
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
بالنقر على هذا المربع، فإنك توافق على الشروط.
import { Checkbox } from "@codefast/ui/checkbox";
import { Field, FieldContent, FieldDescription, FieldGroup, FieldLabel, FieldTitle } from "@codefast/ui/field";
import { Label } from "@codefast/ui/label";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
const translations: Translations = {
en: {
dir: "ltr",
values: {
acceptTerms: "Accept terms and conditions",
acceptTermsDescription: "By clicking this checkbox, you agree to the terms.",
enableNotifications: "Enable notifications",
enableNotificationsDescription: "You can enable or disable notifications at any time.",
},
},
ar: {
dir: "rtl",
values: {
acceptTerms: "قبول الشروط والأحكام",
acceptTermsDescription: "بالنقر على هذا المربع، فإنك توافق على الشروط.",
enableNotifications: "تفعيل الإشعارات",
enableNotificationsDescription: "يمكنك تفعيل أو إلغاء تفعيل الإشعارات في أي وقت.",
},
},
he: {
dir: "rtl",
values: {
acceptTerms: "קבל תנאים והגבלות",
acceptTermsDescription: "על ידי לחיצה על תיבת הסימון הזו, אתה מסכים לתנאים.",
enableNotifications: "הפעל התראות",
enableNotificationsDescription: "אתה יכול להפעיל או להשבית התראות בכל עת.",
},
},
};
export function CheckboxRtl() {
const { dir, t } = useTranslation(translations, "ar");
return (
<FieldGroup className="max-w-sm" dir={dir}>
<Field orientation="horizontal">
<Checkbox id="terms-checkbox-rtl" name="terms-checkbox" />
<Label htmlFor="terms-checkbox-rtl">{t.acceptTerms}</Label>
</Field>
<Field orientation="horizontal">
<Checkbox id="terms-checkbox-2-rtl" name="terms-checkbox-2" defaultChecked />
<FieldContent>
<FieldLabel htmlFor="terms-checkbox-2-rtl">{t.acceptTerms}</FieldLabel>
<FieldDescription>{t.acceptTermsDescription}</FieldDescription>
</FieldContent>
</Field>
<Field orientation="horizontal" data-disabled>
<Checkbox id="toggle-checkbox-rtl" name="toggle-checkbox" disabled />
<FieldLabel htmlFor="toggle-checkbox-rtl">{t.enableNotifications}</FieldLabel>
</Field>
<FieldLabel>
<Field orientation="horizontal">
<Checkbox id="toggle-checkbox-2" name="toggle-checkbox-2" />
<FieldContent>
<FieldTitle>{t.enableNotifications}</FieldTitle>
<FieldDescription>{t.enableNotificationsDescription}</FieldDescription>
</FieldContent>
</Field>
</FieldLabel>
</FieldGroup>
);
}
Table
A control that allows the user to toggle between checked and not checked.
| Name | Role | ||
|---|---|---|---|
| Sarah Chen | sarah.chen@example.com | Admin | |
| Marcus Rodriguez | marcus.rodriguez@example.com | User | |
| Priya Patel | priya.patel@example.com | User | |
| David Kim | david.kim@example.com | Editor |
import { Checkbox } from "@codefast/ui/checkbox";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@codefast/ui/table";
import * as React from "react";
const tableData = [
{
id: "1",
name: "Sarah Chen",
email: "sarah.chen@example.com",
role: "Admin",
},
{
id: "2",
name: "Marcus Rodriguez",
email: "marcus.rodriguez@example.com",
role: "User",
},
{
id: "3",
name: "Priya Patel",
email: "priya.patel@example.com",
role: "User",
},
{
id: "4",
name: "David Kim",
email: "david.kim@example.com",
role: "Editor",
},
];
export function CheckboxInTable() {
const [selectedRows, setSelectedRows] = React.useState<Set<string>>(new Set(["1"]));
const allSelected = selectedRows.size === tableData.length;
const someSelected = selectedRows.size > 0 && !allSelected;
const selectAllState = someSelected ? "indeterminate" : allSelected;
const handleSelectAll = (checked: boolean | "indeterminate") => {
if (checked === true) {
setSelectedRows(new Set(tableData.map((row) => row.id)));
} else {
setSelectedRows(new Set());
}
};
const handleSelectRow = (id: string, checked: boolean) => {
const newSelected = new Set(selectedRows);
if (checked) {
newSelected.add(id);
} else {
newSelected.delete(id);
}
setSelectedRows(newSelected);
};
return (
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-8">
<Checkbox
aria-label="Select all rows"
id="select-all-checkbox"
name="select-all-checkbox"
checked={selectAllState}
onCheckedChange={handleSelectAll}
/>
</TableHead>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{tableData.map((row) => (
<TableRow key={row.id} data-state={selectedRows.has(row.id) ? "selected" : undefined}>
<TableCell>
<Checkbox
id={`row-${row.id}-checkbox`}
name={`row-${row.id}-checkbox`}
checked={selectedRows.has(row.id)}
onCheckedChange={(checked) => handleSelectRow(row.id, checked === true)}
/>
</TableCell>
<TableCell className="font-medium">{row.name}</TableCell>
<TableCell>{row.email}</TableCell>
<TableCell>{row.role}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { Checkbox } from "@codefast/ui/checkbox";
import { Label } from "@codefast/ui/label";
export function CheckboxUsage() {
return (
<div className="flex items-center gap-2">
<Checkbox id="terms" />
<Label htmlFor="terms">Accept terms and conditions</Label>
</div>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- Three visual states — unchecked, checked, and indeterminate (checked="indeterminate") — for "select all"/partially-selected-list patterns.
- Built on Radix Checkbox; the indeterminate state exposes aria-checked="mixed" automatically.
- Reacts to a wrapping Field's disabled/invalid state via group-has-disabled:opacity-50 and aria-invalid: styling.
API reference
Props for each part of the component. All native element props are also forwarded.
Checkbox
Built on Radix Checkbox.
checkedboolean | "indeterminate"The controlled checked state. Pass "indeterminate" for a mixed parent.
defaultCheckedboolean | "indeterminate"The checked state when initially rendered (uncontrolled).
Default
falseonCheckedChange(checked: boolean | "indeterminate") => voidCalled when the checked state changes.
disabledbooleanBlocks interaction and dims the control.
Default
false
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
| Key | Function |
|---|---|
| Tab | Moves focus to the checkbox. |
| Space | Toggles the checkbox. |
- Has role=checkbox; the indeterminate state is exposed as aria-checked="mixed".
- Always associate a Label via htmlFor / id so the control has an accessible name.
- Use a Checkbox (not a Switch) when the change applies only after a form submit.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Use the indeterminate state for a parent that controls a list.
- Let users click the label, not just the box, to toggle.
Don’t
- Don’t use a single checkbox where a Switch better signals an instant setting.
- Don’t rely on the box alone — give it a clear, clickable label.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.