Checkbox

A simple native checkbox prepared for indeterminate states

Dependencies

Source Code

"use client";
 
import { useEffect, useRef } from "react";
 
import { cva } from "@/lib/utils";
import { composeRefs } from "@/lib/compose-refs";
 
interface CheckboxProps
  extends Omit<React.ComponentPropsWithRef<"input">, "type"> {
  indeterminate?: boolean;
}
 
const checkboxStyle = cva({
  base: [
    "appearance-none relative size-5 shrink-0 outline-none rounded-sm border border-border bg-background shadow-xs enabled:cursor-pointer enabled:not-checked:hover:border-border-hard flex items-center justify-center focus-visible:ring-4 ring-ring transition",
    // checked
    "checked:enabled:border-foreground checked:enabled:bg-foreground",
    // indeterminate
    "indeterminate:enabled:border-foreground indeterminate:enabled:bg-foreground",
    // checked checkmark
    "before:absolute checked:before:content-['✓'] before:text-background checked:before:text-xs checked:before:font-bold",
    // indeterminate dash
    "indeterminate:before:w-1.5 indeterminate:before:h-0.5 indeterminate:before:bg-background",
    // disabled
    "disabled:cursor-not-allowed disabled:border-foreground/5 disabled:bg-foreground/10 disabled:checked:before:text-foreground/50 disabled:indeterminate:before:bg-foreground/50",
  ],
});
 
const Checkbox = ({
  ref,
  indeterminate,
  className,
  ...props
}: CheckboxProps) => {
  const internalRef = useRef<HTMLInputElement>(null);
 
  useEffect(() => {
    if (internalRef.current) {
      internalRef.current.indeterminate = !!indeterminate;
    }
  }, [indeterminate]);
 
  return (
    <input
      type="checkbox"
      ref={composeRefs(ref, internalRef)}
      className={checkboxStyle({ className })}
      {...props}
    />
  );
};
 
export { Checkbox };

API Reference

Extends the native input element with type="checkbox".

Examples

Simple

Disabled

All states

Best Practices

  1. Labels:

    • Always provide clear labels
    • Make labels clickable
    • Use consistent label positioning
  2. Accessibility:

    • Ensure keyboard navigation
    • Use fieldset for groups