codefast/ui

Command Palette

Search for a command to run...

Source
Form

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.

13 lines
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.

16 lines
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.

13 lines
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.

Show these items on the desktop:

Select the items you want to show on the desktop.

48 lines
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.

68 lines
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.

13 lines
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.

بالنقر على هذا المربع، فإنك توافق على الشروط.

69 lines
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.

NameEmailRole
Sarah Chensarah.chen@example.comAdmin
Marcus Rodriguezmarcus.rodriguez@example.comUser
Priya Patelpriya.patel@example.comUser
David Kimdavid.kim@example.comEditor
94 lines
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.

11 lines
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.

Checkbox

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).

Defaultfalse

onCheckedChange(checked: boolean | "indeterminate") => void

Called when the checked state changes.

disabledboolean

Blocks interaction and dims the control.

Defaultfalse

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

KeyFunction
TabMoves focus to the checkbox.
SpaceToggles 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.