Checkbox

Accessible checkbox with checked, indeterminate and disabled states.

import { Checkbox }from "@/components/ui"

Specification

Toggles a single boolean option, supporting checked, indeterminate and disabled states.

Props (5)
Every prop this component accepts, its type, default, and how to use it.
PropTypeDefaultDescription
checkedboolean—Whether the checkbox is checked.
indeterminateboolean—Shows the mixed/dash state, typically for a partially-selected group.
disabledboolean—Disables the checkbox.
onChange() => void—Called when the checkbox is toggled; update state yourself.
ariaLabelstring—Accessible label; required since the control renders no visible text.

States

Full source
The complete component, read from src/components/ui/checkbox.tsx.
checkbox.tsx
"use client";

import { Check as CheckIcon } from "lucide-react";
import { cn } from "@/lib/utils";

interface CheckboxProps {
  checked?: boolean;
  indeterminate?: boolean;
  disabled?: boolean;
  onChange?: () => void;

  ariaLabel?: string;
}

export function Checkbox({ checked, indeterminate, disabled, onChange, ariaLabel }: CheckboxProps) {
  const on = checked || indeterminate;
  return (
    <button
      type="button"
      disabled={disabled}
      onClick={onChange}
      role="checkbox"
      aria-checked={indeterminate ? "mixed" : Boolean(checked)}
      aria-label={ariaLabel}
      className={cn(
        "flex h-4 w-4 items-center justify-center rounded-sm border transition-colors",
        "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent",
        "disabled:cursor-not-allowed disabled:opacity-50",
        on ? "border-accent bg-accent" : "border-border bg-surface",
        !on && disabled && "bg-bg-secondary"
      )}
    >
      {indeterminate ? (
        <span className="h-0.5 w-2 rounded-full bg-on-accent" />
      ) : checked ? (
        <CheckIcon size={11} strokeWidth={3} className="text-on-accent" />
      ) : null}
    </button>
  );
}