Skip to main content

Overview

The Checkbox component provides a flexible checkbox input with support for checked, unchecked, and indeterminate states, along with labels and error handling.

Basic Usage

  • Code
  • Preview
import { Checkbox } from '@peppermint-design/devreadykit';
import type { CheckboxValue } from "@/ui/checkbox";

export default function App() {
  const [checked, setChecked] = useState<CheckboxValue>("unchecked");

  return (
    <Checkbox
      value={checked}
      onValueChange={setChecked}
      label="Accept terms and conditions"
    />
  );
}

States

Checked

  • Code
  • Preview
<Checkbox value="checked" label="Checked checkbox" />
<Checkbox value="unchecked" label="Unchecked checkbox" />
<Checkbox 
  value="indeterminate" 
  label="Indeterminate checkbox"
  allowIndeterminate
/>

With Labels

Text Label

  • Code
  • Preview
<Checkbox value="checked" label="I agree to the terms" />
<Checkbox value="unchecked" label="Subscribe to newsletter" />

<Checkbox 
  value="checked" 
  label={
    <span>
      I agree to the <a href="/terms" className="text-blue-500 underline">Terms of Service</a>
    </span>
  } 
/>

Visual States

Focus

  • Code
  • Preview
<Checkbox 
  value="unchecked" 
  label="Focused checkbox"
  visualState="focus"
/>

<Checkbox 
  value="unchecked" 
  label="Error checkbox"
  visualState="error"
  errorMessage="This field is required"
/>

<Checkbox 
  value="checked" 
  label="Disabled checked"
  visualState="disabled"
/>
<Checkbox 
  value="unchecked" 
  label="Disabled unchecked"
  visualState="disabled"
/>

Props

PropTypeDefaultDescription
value"unchecked" | "checked" | "indeterminate""unchecked"Checkbox state
onValueChange(value: CheckboxValue, event: ChangeEvent) => voidChange handler
visualState"rest" | "focus" | "error" | "disabled" | "default" | "focused""rest"Visual state
labelReact.ReactNodeLabel content
requiredbooleanfalseRequired field indicator
errorMessageReact.ReactNodeError message
checkIconReact.ReactElement<SVGProps><Checkmark />Check icon
indeterminateIconReact.ReactElement<SVGProps>Default lineIndeterminate icon
allowIndeterminatebooleanfalseAllow indeterminate state
disabledbooleanfalseDisabled state
classNamestringAdditional CSS classes

Examples

Form with Multiple Checkboxes

  • Code
  • Preview
function PreferencesForm() {
  const [preferences, setPreferences] = useState({
    newsletter: "unchecked",
    notifications: "checked",
    analytics: "unchecked"
  });

  const handleChange = (key: string) => (value: CheckboxValue) => {
    setPreferences(prev => ({ ...prev, [key]: value }));
  };

  return (
    <form className="space-y-4">
      <div className="space-y-3">
        <h3 className="font-medium">Preferences</h3>
        
        <Checkbox
          value={preferences.newsletter}
          onValueChange={handleChange('newsletter')}
          label="Subscribe to newsletter"
        />
        
        <Checkbox
          value={preferences.notifications}
          onValueChange={handleChange('notifications')}
          label="Enable push notifications"
        />
        
        <Checkbox
          value={preferences.analytics}
          onValueChange={handleChange('analytics')}
          label="Allow analytics tracking"
        />
      </div>
    </form>
  );
}

Checkbox with Indeterminate Support

  • Code
  • Preview
import { useMemo, useState } from "react";
import Checkbox, { type CheckboxValue } from "@peppermint-design/devreadykit";

type Task = { id: number; name: string; completed: CheckboxValue };

const INITIAL_TASKS: Task[] = [
  { id: 1, name: "Task 1", completed: "checked" },
  { id: 2, name: "Task 2", completed: "unchecked" },
  { id: 3, name: "Task 3", completed: "checked" },
];

export default function CheckboxIndeterminateExample() {
  const [tasks, setTasks] = useState<Task[]>(INITIAL_TASKS);

  const { allCompleted, selectAllValue } = useMemo(() => {
    const allChecked = tasks.every((t) => t.completed === "checked");
    const someChecked = tasks.some((t) => t.completed === "checked");
    const selectAll: CheckboxValue = allChecked
        ? "checked"
        : someChecked
            ? "indeterminate"
            : "unchecked";

    return {
      allCompleted: allChecked,
      selectAllValue: selectAll,
    };
  }, [tasks]);

  const handleSelectAll = (_: CheckboxValue) => {
    const next: CheckboxValue = allCompleted ? "unchecked" : "checked";
    setTasks((prev) => prev.map((task) => ({ ...task, completed: next })));
  };

  const handleTaskChange = (id: number) => (value: CheckboxValue) => {
    setTasks((prev) =>
        prev.map((task) =>
            task.id === id ? { ...task, completed: value } : task
        )
    );
  };

  return (
      <div className="flex items-center flex-col gap-4 w-full">
        <div className="w-full max-w-md space-y-3">
          <Checkbox
              value={selectAllValue}
              onValueChange={handleSelectAll}
              label="Select All"
              allowIndeterminate
          />

          {tasks.map((task) => (
              <Checkbox
                  key={task.id}
                  value={task.completed}
                  onValueChange={handleTaskChange(task.id)}
                  label={task.name}
              />
          ))}
        </div>
      </div>
  );
}

Validation Example

  • Code
  • Preview
function TermsCheckbox() {
  const [accepted, setAccepted] = useState("unchecked");
  const [error, setError] = useState("");

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    
    if (accepted !== "checked") {
      setError("You must accept the terms to continue");
      return;
    }
    
    setError("");
    // Submit form
  };

  return (
    <form onSubmit={handleSubmit}>
      <Checkbox
        value={accepted}
        onValueChange={(value) => {
          setAccepted(value);
          if (error && value === "checked") {
            setError("");
          }
        }}
        label="I accept the terms and conditions"
        required
        visualState={error ? "error" : "rest"}
        errorMessage={error}
      />
      
      <button type="submit" className="mt-4 px-4 py-2 bg-blue-500 text-white rounded">
        Continue
      </button>
    </form>
  );
}
I