Skip to content

Checkbox

A binary (or indeterminate) choice, typically submitted with a form.

Stablev0.4.2added in v0.1.0@garn/ui/checkbox
On this page

Default

Default checkbox.

Sizing

Box size, mapped to the fixed --garn-box-* geometry (identity, not density).

States

Interactive states, toggled live.

Labeled

Checkbox with a clickable label.

Indeterminate

The third state.

Installation

garn is copy-in — the garn CLI writes this component's source into your project, so you own and can edit it. It resolves any base files and installs the npm dependencies below for you.

terminal
npx garn-ui add checkbox
First time? Set up garn in your project
Registry
checkbox
Deps
@radix-ui/react-checkboxclass-variance-authoritylucide-react
Registry deps
utils

Import

import { Checkbox } from "@garn/ui/checkbox";

Props

The component's public props and their types.

size
Description
No description.
Type
"lg" | "md" | "sm"
Default
md

Plus 294 inherited native <button> attributes.

Styling

Target these data-slots and states, and remap these tokens, to restyle without forking the component.

[data-slot="checkbox"]

States

SelectorState
[data-state=checked]checked
[data-state=indeterminate]indeterminate
:focus-visiblefocus-visible
[disabled]disabled
PropertyTokenTier
size--garn-box-mdsemantic · identity
fill--garn-brand-solidbrandablesemantic
ring--garn-ringbrandablesemantic

When to use

Toggle an independent boolean, or select several items from a list.

Reach for something else when

  • An on/off that takes effect immediately (use a switch)
  • one choice among mutually exclusive options (use radio-group).

Overview

Checkbox toggles a single independent boolean ("Remember me", "I agree") or selects several items from a list where choices aren't mutually exclusive. It supports an indeterminate state for a parent that summarizes a partially-selected group. Reach for RadioGroup instead when exactly one of a few options must be chosen, or Switch when the change applies immediately with no Save step.

Guidelines

Pair every checkbox with a Label. The box alone is a tiny hit target with no accessible name; an associated Label both names it and extends the click area to the text. Keep the wording affirmative so checking it means "yes" — "Send me updates", not "Don't send updates".

Use indeterminate only for a summary parent. It's the visual for "some but not all children selected" in a select-all pattern — never a third user-settable value. Drive it from the children's state; clicking it should resolve to all-on or all-off.

Don't make a checkbox apply instantly. Checkboxes imply a pending choice confirmed on submit; if the toggle takes effect the moment it changes (a setting), that's a Switch. Group related checkboxes under a shared heading or fieldset so the relationship is announced.

Best practices

Do
  • Pair with a clickable label and keep the hit target generous.
  • Use the indeterminate state for a parent that summarizes a partially-checked group.
Don't
  • Don't use a checkbox for a setting that applies instantly — that's a switch.
  • Don't group mutually exclusive options as checkboxes — use radio-group.

Content guidelines

  • Phrase the label as the positive choice the user opts into (“Email me about updates”).
  • Use sentence case, keep it to a short phrase, and make the whole label clickable.
  • In a list, keep labels parallel and ordered so the set is easy to scan.

Troubleshooting

The checkbox won't change when clicked.

Cause. Passing checked without onCheckedChange makes it controlled and frozen.

Fix. Use defaultChecked for uncontrolled, or pass checked + onCheckedChange.

Accessibility

Role
checkbox
ARIA APG
checkbox
Focus
outline-hidden + 2px ring with a 2px offset.

Accessibility requirements

warn

Give every checkbox an accessible label (a clickable <label htmlFor>, or aria-label).

when no associated <label> or aria-label/aria-labelledby

Keyboard

SpaceToggle checked / unchecked.
A1.3.1Info and RelationshipsA2.1.1KeyboardA4.1.2Name, Role, Value
  • data-state reflects checked / unchecked / indeterminate (Radix maps it to aria-checked).