Skip to content
Dethink Components

ComponentsCheckbox

Checkbox

Let users turn individual options on or off.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
import { Checkbox } from "@dethink/components";

Try the examples, then open the code to use them in your app.

With Field anatomy

Field, FieldControl, FieldLabel, and FieldDescription wire the label association and description announcement.

An adoption and reliability digest every Monday.

Show sourceexamples/checkbox/basic.tsx
examples/checkbox/basic.tsx
"use client";

import {
  Checkbox,
  Field,
  FieldContent,
  FieldControl,
  FieldDescription,
  FieldLabel,
} from "@dethink/components";

export function CheckboxBasic() {
  return (
    <div className="mx-auto max-w-sm space-y-4">
      <Field id="cb-updates" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox name="updates" value="yes" defaultChecked />
        </FieldControl>
        <FieldLabel>Receive product updates</FieldLabel>
      </Field>
      <Field id="cb-summary" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox name="summary" value="enabled" />
        </FieldControl>
        <FieldContent>
          <FieldLabel>Weekly summary</FieldLabel>
          <FieldDescription>
            An adoption and reliability digest every Monday.
          </FieldDescription>
        </FieldContent>
      </Field>
    </div>
  );
}

States and sizes

Checked, indeterminate, disabled, invalid, and the three control sizes.

Show sourceexamples/checkbox/states.tsx
examples/checkbox/states.tsx
"use client";

import { Checkbox, Field, FieldControl, FieldLabel } from "@dethink/components";

export function CheckboxStates() {
  return (
    <div className="mx-auto grid max-w-md gap-4 sm:grid-cols-2">
      <Field id="cb-checked" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox defaultChecked />
        </FieldControl>
        <FieldLabel>Checked</FieldLabel>
      </Field>
      <Field id="cb-indeterminate" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox checked="indeterminate" readOnly />
        </FieldControl>
        <FieldLabel>Indeterminate</FieldLabel>
      </Field>
      <Field id="cb-disabled" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox disabled defaultChecked />
        </FieldControl>
        <FieldLabel>Disabled</FieldLabel>
      </Field>
      <Field id="cb-invalid" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox invalid required />
        </FieldControl>
        <FieldLabel>Invalid, required</FieldLabel>
      </Field>
      <Field id="cb-sm" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox controlSize="sm" defaultChecked />
        </FieldControl>
        <FieldLabel>Small</FieldLabel>
      </Field>
      <Field id="cb-lg" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox controlSize="lg" defaultChecked />
        </FieldControl>
        <FieldLabel>Large</FieldLabel>
      </Field>
    </div>
  );
}

Examples that combine components for common tasks.

Permissions tree with tri-state parent

The parent checkbox derives its state from the children — checked, unchecked, or indeterminate — and clicking it snaps the group to the obvious next state. Screen readers announce the mixed state natively.

1 of 4 permissions granted

Show sourceexamples/checkbox/recipe-permissions-tree.tsx
examples/checkbox/recipe-permissions-tree.tsx
"use client";

import { useState } from "react";
import {
  Checkbox,
  Field,
  FieldControl,
  FieldLabel,
  type CheckboxCheckedState,
} from "@dethink/components";

const permissions = [
  { id: "read", label: "Read repositories" },
  { id: "write", label: "Write and push" },
  { id: "admin", label: "Manage settings" },
  { id: "billing", label: "View billing" },
];

/**
 * Classic tri-state pattern: the parent reflects its children — checked when
 * all are, unchecked when none are, and "indeterminate" in between — and
 * clicking it snaps the whole group to the obvious next state.
 */
export function CheckboxRecipePermissionsTree() {
  const [granted, setGranted] = useState<Set<string>>(new Set(["read"]));

  const parentState: CheckboxCheckedState =
    granted.size === 0
      ? false
      : granted.size === permissions.length
        ? true
        : "indeterminate";

  return (
    <div className="mx-auto max-w-sm space-y-3">
      <Field id="perm-all" orientation="horizontal">
        <FieldControl asChild>
          <Checkbox
            checked={parentState}
            onCheckedChange={() =>
              setGranted(
                parentState === true
                  ? new Set()
                  : new Set(permissions.map((permission) => permission.id)),
              )
            }
          />
        </FieldControl>
        <FieldLabel className="font-medium">All permissions</FieldLabel>
      </Field>
      <div className="border-border space-y-3 border-l pl-6">
        {permissions.map((permission) => (
          <Field
            key={permission.id}
            id={`perm-${permission.id}`}
            orientation="horizontal"
          >
            <FieldControl asChild>
              <Checkbox
                checked={granted.has(permission.id)}
                onCheckedChange={(checked) =>
                  setGranted((current) => {
                    const next = new Set(current);
                    if (checked === true) {
                      next.add(permission.id);
                    } else {
                      next.delete(permission.id);
                    }
                    return next;
                  })
                }
              />
            </FieldControl>
            <FieldLabel>{permission.label}</FieldLabel>
          </Field>
        ))}
      </div>
      <p className="text-muted-foreground text-sm">
        {granted.size} of {permissions.length} permissions granted
      </p>
    </div>
  );
}

Checkbox renders a real input element, so everything a native checkbox accepts works here.

Checkbox props
PropWhat it doesDefault
checkedboolean | "indeterminate"Controlled state. "indeterminate" renders the mixed state and announces as such.Not set
defaultCheckedboolean | "indeterminate"The starting state when the component manages its own state.false
onCheckedChange(checked: boolean | "indeterminate") => voidCalled with the new state when the user toggles the control.Not set
controlSize"sm" | "md" | "lg"Sets the control size to match other form fields."md"
invalidbooleanShows an error style and marks the field as invalid for screen readers.false
…native input propsInputHTMLAttributesRenders a real <input type="checkbox">: name, value, disabled, required, readOnly, and form behavior are native.Not set