Skip to main content
Gremorie

Checkbox

Binary or tri-state selection control built on Radix Checkbox, with indeterminate support and form-payload semantics.

Overview

Checkbox is built on @radix-ui/react-checkbox. It supports the three canonical states - unchecked, checked, and indeterminate - via Radix's checked prop, which accepts true | false | "indeterminate". The indicator renders a CheckIcon from lucide.

Use Checkbox when the value is part of a form submission (terms acceptance, multi-select filters, table row selection). Reach for Switch instead when the change takes effect immediately, without submission (notifications on / off, dark mode).

Preview

'use client';import { Checkbox, Label } from '@gremorie/rx-forms';export function CheckboxPreview() {  return (    <div className="flex items-center gap-2">      <Checkbox id="cb-demo" defaultChecked />      <Label htmlFor="cb-demo">Subscribe to the changelog</Label>    </div>  );}

Anatomy

Checkbox   4×4 rounded box; renders the check glyph when checked

Installation

bash npx gremorie@latest add rx-checkbox
bash pnpm dlx gremorie@latest add rx-checkbox
bash yarn dlx gremorie@latest add rx-checkbox
bash bunx --bun gremorie@latest add rx-checkbox

Usage

import { Checkbox, Label } from "@gremorie/rx-forms";

export function Example() {
  return (
    <div className="flex items-center gap-2">
      <Checkbox id="terms" />
      <Label htmlFor="terms">Accept terms and conditions</Label>
    </div>
  );
}

The Angular edition of this component ships from source today (see the workbench for the side-by-side); its registry entry is coming next.

API

<Checkbox>

PropTypeDefaultDescription
checkedboolean | "indeterminate"-Controlled checked state. Pass "indeterminate" for the dash indicator.
defaultCheckedboolean | "indeterminate"-Uncontrolled initial state.
onCheckedChange(checked: boolean | "indeterminate") => void-Fires when the user toggles the checkbox.
disabledbooleanfalseDisables interaction.
requiredbooleanfalseMark as required for form submission.
namestring-Form field name.
valuestring"on"Form field value when checked.
aria-invalidboolean-Switches border / ring to destructive token.

Forwards all props to CheckboxPrimitive.Root and renders a CheckboxPrimitive.Indicator with a CheckIcon inside. The data-state attribute (unchecked, checked, indeterminate) drives the visual surface.

Composition

  1. Always pair with a <Label> via htmlFor matching the checkbox's id. The label is the affordance most users click.
  2. Inside a <Form>, use <FormField> with <FormControl> so ARIA wiring happens automatically.
  3. For "select all" patterns, drive the master checkbox into indeterminate when some (but not all) children are checked.

Variations

With label and description

The canonical form pattern: checkbox, label, and an optional helper line. Clicking the label toggles the control.

You agree to our Terms of Service and Privacy Policy.

'use client';import { Checkbox, Label } from '@gremorie/rx-forms';export function CheckboxWithLabelPreview() {  return (    <div className="flex items-start gap-3">      <Checkbox id="cb-terms" defaultChecked />      <div className="grid gap-1.5 leading-none">        <Label htmlFor="cb-terms">Accept terms and conditions</Label>        <p className="text-sm text-muted-foreground">          You agree to our Terms of Service and Privacy Policy.        </p>      </div>    </div>  );}

States

The three canonical states - unchecked, checked, and indeterminate - side by side. Indeterminate renders the dash indicator and announces as "mixed".

'use client';import { Checkbox, Label } from '@gremorie/rx-forms';export function CheckboxStatesPreview() {  return (    <div className="flex flex-col gap-3">      <div className="flex items-center gap-2">        <Checkbox id="cb-unchecked" />        <Label htmlFor="cb-unchecked">Unchecked</Label>      </div>      <div className="flex items-center gap-2">        <Checkbox id="cb-checked" defaultChecked />        <Label htmlFor="cb-checked">Checked</Label>      </div>      <div className="flex items-center gap-2">        <Checkbox id="cb-indeterminate" defaultChecked="indeterminate" />        <Label htmlFor="cb-indeterminate">Indeterminate</Label>      </div>    </div>  );}

Indeterminate "select all"

Master checkbox flips to indeterminate when some children are selected. Clicking it should clear all when indeterminate / checked, and select all when unchecked.

function SelectAll({ items, selected, onChange }) {
  const allSelected = selected.length === items.length;
  const someSelected = selected.length > 0 && !allSelected;

  return (
    <div className="flex items-center gap-2">
      <Checkbox
        id="select-all"
        checked={allSelected ? true : someSelected ? 'indeterminate' : false}
        onCheckedChange={(value) => {
          onChange(value === true ? items.map((i) => i.id) : []);
        }}
      />
      <Label htmlFor="select-all">Select all</Label>
    </div>
  );
}

Disabled

Label reads peer-disabled and fades automatically when the sibling peer-marked checkbox is disabled. Radix's Checkbox.Root includes the peer class, so both the unchecked and checked locked states stay legible.

'use client';import { Checkbox, Label } from '@gremorie/rx-forms';export function CheckboxDisabledPreview() {  return (    <div className="flex flex-col gap-3">      <div className="flex items-center gap-2">        <Checkbox id="cb-disabled-off" disabled />        <Label htmlFor="cb-disabled-off">Disabled, unchecked</Label>      </div>      <div className="flex items-center gap-2">        <Checkbox id="cb-disabled-on" disabled defaultChecked />        <Label htmlFor="cb-disabled-on">Disabled, checked</Label>      </div>    </div>  );}

Accessibility

  • Native semantics: Radix renders role="checkbox" with aria-checked reflecting the state (true, false, mixed for indeterminate).
  • Keyboard: Space toggles the checkbox; Tab / Shift+Tab move focus.
  • Label association: clicking the <Label> toggles the checkbox via the standard htmlFor mechanism.
  • Indeterminate: aria-checked="mixed" is announced as "mixed" by screen readers, signalling partial selection.
  • Required + invalid: combine required with aria-invalid="true" and an error message; the destructive ring kicks in automatically.
  • Switch - immediate-effect toggle (vs form-submission selection)
  • Radio Group - single-select cousin
  • Label - the canonical companion
  • Form - wire Checkbox into react-hook-form

On this page