Skip to content
shadcn.io is not affiliated with official shadcn/ui

Shadcn Checkbox for React and Tailwind

Control for multiple selections in a set.

You'll receive a notification when someone posts a comment

Installation

bunx --bun shadcn@latest add https://kit.dev/r/checkbox.json

Anatomy

Checkbox
├── Control (baked in)
│   ├── Indicator (checked)
│   └── Indicator (indeterminate)
└── HiddenInput (baked in)

CheckboxGroup
└── Checkbox

Checkbox is Ark Checkbox.Root (a label). Control, indicators, and the hidden native input are baked in. Put the visible name on Field / FieldLabel, not inside the box.

useCheckbox is the machine hook for CheckboxRootProvider. useCheckboxContext / CheckboxContext is in-tree. useCheckboxGroup is for CheckboxGroupProvider.

Usage

import { Checkbox } from "@/components/ui/checkbox";
import { Field, FieldLabel } from "@/components/ui/field";
<Field orientation="horizontal">
  <Checkbox />
  <FieldLabel>Accept terms and conditions</FieldLabel>
</Field>

Do not wrap Checkbox in another label. The root is already a label associated with the hidden input. FieldLabel uses Field's htmlFor (not a nested label around the box).

Controlled

Use checked and onCheckedChange. checked is boolean | "indeterminate".

Default checked

Uncontrolled initial state with defaultChecked.

Root Provider

Use useCheckbox with CheckboxRootProvider when you need the API outside the tree. Pass machine options (defaultChecked, disabled, name, …) to useCheckbox(), not to the provider.

Context

CheckboxContext / useCheckboxContext only work under Checkbox or CheckboxRootProvider. shadcn.io bakes the control into a size-4 root, so the usual way to read state outside the box is useCheckbox + CheckboxRootProvider.

States

Invalid

Set invalid on Checkbox or on Field (Field forwards invalid into the checkbox).

Disabled

Read-only

readOnly keeps the value visible but not changeable.

Indeterminate

Set checked="indeterminate" (not a separate indeterminate prop on the root). The baked indicator uses MinusIcon for that state.

Examples

With description

Field helper and error

Card

Wrap Field in FieldLabel for a full-width selectable card.

Form

Pass name and value (default "on"). The hidden input submits with the form. Unchecked boxes are omitted from FormData.

Library-specific forms (React Hook Form, TanStack Form, Formisch) live on the form pages.

Checkbox Group

CheckboxGroup owns the selected values. Each Checkbox needs a value. The group sets name, disabled, invalid, and readOnly on items.

<CheckboxGroup defaultValue={["hard-disks"]} name="desktop">
  <Field orientation="horizontal">
    <Checkbox value="hard-disks" />
    <FieldLabel>Hard disks</FieldLabel>
  </Field>
</CheckboxGroup>

Controlled group

Use value and onValueChange. The handler receives string[], not a details object.

Group provider

useCheckboxGroup + CheckboxGroupProvider when you need the group API outside the tree.

Invalid group

invalid on CheckboxGroup applies to every item.

Max selected

maxSelectedValues disables remaining items once the cap is reached.

Select all

A parent checkbox outside the group, with checked="indeterminate" when the selection is partial.

Group in a form

Set name on the group. Read selected values with FormData.getAll(name).

Guides

asChild

The root is a label. If you set asChild, the child must also be a label or the HTML and accessibility structure is invalid.

Pages vs Field

Prefer Field + FieldLabel for the visible name. Do not use Ark Checkbox.Label inside the shadcn.io box — the root is already the visual control.

API Reference

shadcn.io wraps Ark UI Checkbox. Control, checked/indeterminate indicators, and HiddenInput are baked into Checkbox / CheckboxRootProvider.

asChild merges props onto a single child element.

Checkbox

Root. Renders a label. shadcn.io also sets role="checkbox" so Field horizontal alignment can target the box.

PropTypeDefaultDescription
asChildbooleanfalseRender the child element instead of a label. The child must be a label.
checkedboolean | "indeterminate"-Controlled checked state.
classNamestring-Class names on the root (the visual box).
defaultCheckedboolean | "indeterminate"-Uncontrolled initial state.
dir"ltr" | "rtl"-Text direction. Usually inherited from LocaleProvider.
disabledboolean-Disable the checkbox. Also inherited from Field / CheckboxGroup.
formstring-form id for the hidden input.
idstring-Unique id for the checkbox machine.
idsPartial<{ root: string; hiddenInput: string; control: string; label: string }>-Element ids for composition. Field sets label and hiddenInput when present.
invalidboolean-Invalid state. Also inherited from Field / CheckboxGroup.
namestring-Hidden input name. Inherited from CheckboxGroup when grouped.
onCheckedChange(details: CheckedChangeDetails) => void-Checked state changed. { checked: boolean | "indeterminate" }.
readOnlyboolean-Visible but not editable.
requiredboolean-Native required. Also inherited from Field.
tabIndexnumber-Applied to the hidden input, not the label.
valuestring"on"Hidden input value. Required for items in a group.
AttributeDescription
data-slotcheckbox
data-scopecheckbox
data-partroot
data-state"checked", "unchecked", or "indeterminate"
data-focusPresent when focused
data-focus-visiblePresent when focus is visible
data-hoverPresent when hovered
data-activePresent while pressed
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only
data-requiredPresent when required

The native input is visually hidden. The control is aria-hidden; check and minus icons are aria-hidden.

Control / Indicator / HiddenInput

Baked into Checkbox and CheckboxRootProvider. Not used as separate imports in typical usage. CheckboxIndicator remains exported if you replace the chrome.

AttributeDescription
data-slotcheckbox-control / checkbox-indicator
data-scopecheckbox
data-partcontrol / indicator
data-stateSame as the root

CheckboxIndicator accepts indeterminate to show only in the indeterminate state.

CheckboxRootProvider

Takes the API from useCheckbox. Same baked control as Checkbox. Renders a label.

PropTypeDefaultDescription
valueUseCheckboxReturnrequiredReturn value of useCheckbox().
asChildbooleanfalseRender the child element instead of a label.
classNamestring-Class names on the root.
tabIndexnumber-Applied to the hidden input.

Pass defaultChecked, disabled, name, and other machine options to useCheckbox(), not to CheckboxRootProvider.

CheckboxGroup

Manages a set of checkboxes. Renders a div with role="group".

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the group.
defaultValuestring[][]Uncontrolled selected values.
disabledboolean-Disable every item. Also inherited from Fieldset.
invalidboolean-Invalid state on every item. Also inherited from Fieldset.
maxSelectedValuesnumber-Cap on selected values. Further items become disabled.
namestring-Shared name for hidden inputs.
onValueChange(value: string[]) => void-Selected values changed. Receives string[], not { value }.
readOnlyboolean-Read-only on every item.
valuestring[]-Controlled selected values.
AttributeDescription
data-slotcheckbox-group
data-scopecheckbox
data-partgroup

CheckboxGroupProvider

Takes the API from useCheckboxGroup.

PropTypeDefaultDescription
valueUseCheckboxGroupReturnrequiredReturn value of useCheckboxGroup().
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the group.

Pass defaultValue, name, maxSelectedValues, and other options to useCheckboxGroup(), not to the provider.

useCheckbox

Creates the checkbox API for CheckboxRootProvider. Accepts the same machine options as Checkbox except layout-only props.

const checkbox = useCheckbox({ defaultChecked: true });
checkbox.setChecked(false);
checkbox.toggleChecked();

Inherits disabled, invalid, required, and ids from Field when used under Field. Inherits item props from CheckboxGroup when grouped.

CheckboxContext / useCheckboxContext

Render-prop or hook access inside Checkbox or CheckboxRootProvider.

PropertyTypeDescription
checkedbooleanWhether the box is fully checked (false when indeterminate).
checkedStateboolean | "indeterminate"The full checked state.
indeterminatebooleanWhether the state is indeterminate.
disabledbooleanWhether the checkbox is disabled.
focusedbooleanWhether the hidden input is focused.
setChecked(checked: boolean | "indeterminate") => voidSet the checked state.
toggleChecked() => voidToggle checked.

CheckboxContext children: (context) => ReactNode.

useCheckboxGroup / useCheckboxGroupContext

useCheckboxGroup creates the group store for CheckboxGroupProvider. useCheckboxGroupContext reads it inside a group.

PropertyTypeDescription
valuestring[]Selected values.
namestring | undefinedShared input name.
disabled / readOnly / invalidbooleanGroup flags.
setValue(value: string[]) => voidReplace the selection.
addValue / toggleValue(value: string) => voidAdd or toggle one value.
isChecked(value: string | undefined) => booleanWhether a value is selected.
getItemProps(props: { value?: string }) => …Props merged into each checkbox.

Accessibility

Complies with the Checkbox WAI-ARIA design pattern. The hidden native input type="checkbox" is the accessible control. The root label is associated with it. Give every checkbox a visible name (FieldLabel). Groups should have a legend (FieldLegend / FieldSet).

Prefer Field so disabled, invalid, and required stay in sync. Decorative check and minus icons are aria-hidden.

Keyboard support

KeyDescription
TabMove focus to the checkbox (the hidden input).
Shift + TabMove focus to the previous control.
SpaceToggle checked. From indeterminate, typically becomes checked.