Skip to content
VEXA
Forms

Checkbox

Client leaf

A control that toggles a boolean or shows an indeterminate state.

Live preview

Rendered from the installed VEXA source — the same files you receive.

Installation

Adds the component source to your project. Registry dependencies are pulled in automatically.

npx shadcn@latest add https://vexa.valfiguer.com/r/checkbox.json

Installs to components/vexa/checkbox.tsx

npm dependencies

  • @radix-ui/react-checkbox

Registry dependencies

  • @vexa/utils

Other VEXA items this component needs. Install them first — they are resolved automatically by the CLI.

Source

Copy this into its target path, or install it with the command above.

components/vexa/checkbox.tsx
"use client";

import type * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";

import { cn } from "@/lib/utils";

function CheckIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      aria-hidden="true"
      className={className}
    >
      <path
        d="M4 12.5 9 17.5 20 6.5"
        stroke="currentColor"
        strokeWidth="2.5"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function MinusIcon({ className }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 24 24"
      fill="none"
      aria-hidden="true"
      className={className}
    >
      <path
        d="M6 12h12"
        stroke="currentColor"
        strokeWidth="2.5"
        strokeLinecap="round"
      />
    </svg>
  );
}

/**
 * Checkbox — a control that toggles a boolean, or represents an indeterminate
 * (mixed) state. Built on Radix so the role, `aria-checked` (including
 * `"mixed"`), keyboard model (Space toggles), and label association are handled.
 *
 * Client leaf: the `"use client"` boundary lives here. Pair with `<Label>` via
 * a shared `id`, or wrap the label text so a click on it toggles the control.
 * The checked/indeterminate state is conveyed by an icon, not color alone.
 */
export function Checkbox({
  className,
  ...props
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
  return (
    <CheckboxPrimitive.Root
      data-slot="checkbox"
      className={cn(
        "group peer size-5 shrink-0 rounded-vexa-sm border border-vexa-input bg-vexa-surface-raised shadow-vexa-sm",
        "transition-[color,background-color,border-color,box-shadow] duration-[var(--vexa-duration-fast)] ease-[var(--vexa-ease-standard)]",
        "outline-none focus-visible:ring-2 focus-visible:ring-vexa-ring focus-visible:ring-offset-2 focus-visible:ring-offset-vexa-background",
        "disabled:cursor-not-allowed disabled:opacity-50",
        "data-[state=checked]:border-vexa-primary data-[state=checked]:bg-vexa-primary data-[state=checked]:text-vexa-primary-foreground",
        "data-[state=indeterminate]:border-vexa-primary data-[state=indeterminate]:bg-vexa-primary data-[state=indeterminate]:text-vexa-primary-foreground",
        "aria-[invalid=true]:border-vexa-danger",
        className,
      )}
      {...props}
    >
      <CheckboxPrimitive.Indicator
        data-slot="checkbox-indicator"
        className="flex items-center justify-center text-current"
      >
        <CheckIcon className="size-3.5 group-data-[state=indeterminate]:hidden" />
        <MinusIcon className="hidden size-3.5 group-data-[state=indeterminate]:block" />
      </CheckboxPrimitive.Indicator>
    </CheckboxPrimitive.Root>
  );
}

Details

Example

tsx
<div className="flex items-center gap-2"><Checkbox id="terms" /><Label htmlFor="terms">Accept terms</Label></div>

Accessibility

Radix supplies role=checkbox, aria-checked (including "mixed"), and Space to toggle. Checked/indeterminate shown by an icon, not color alone. Associate a <Label>.