shadcn.io is not affiliated with official shadcn/ui
The box and its label sit on one line. The box alone is a small target, so the label has to toggle it too.
Use it when the choice waits for the form to be submitted. A Switch fits a setting that applies the moment it changes. In React, Base UI tracks a mixed state when only some children are on.
Installation
Anatomy

Checkbox
├── CheckboxLabel
├── checkboxVariants
└── CheckboxIndicatorUsage
import { Checkbox } from "@/components/ui/checkbox";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";<FieldGroup className="w-fit whitespace-nowrap">
<Field orientation="horizontal">
<Checkbox name="terms" />
<FieldLabel>Accept terms</FieldLabel>
</Field>
</FieldGroup>Accessibility
- Space
- Toggle the checkbox.
Demos
Invalid
aria-invalid on the checkbox and data-invalid on the field turn on the error styling for a terms box that was left empty. The words of the error still have to live somewhere the person can read. Use the invalid state after submit, when acceptance was required and did not happen. A disabled box is not an error: it is a choice the person cannot make.
Basic
One checkbox, one label, a name for the form. That is the smallest useful version: the label is the hit target, and the name is how the value shows up on submit. Start here for a single agreement. Add a description only when the label's words are not enough to explain the consequence, and a group only when there are several related boxes.
Description
The terms box is already checked, and a second line under the label says you agree to the terms. FieldContent stacks the label and that description so the checkbox stays aligned to the first line, not the whole block. Use the extra line when the consequence is easy to miss, such as a billing change or a data share. Keep that line to the consequence, shorter than a paragraph.
Disabled
Notifications cannot be toggled. disabled is on the checkbox and data-disabled is on the field, so the label fades with the box and clicks do nothing. Use this for a preference the current plan or role does not allow, and say why nearby if it is not obvious. Hiding the row is the alternative when the choice should not even be visible.
Group
Hard disks and external disks start checked, servers does not, and the set is labeled Desktop. Each box has its own name, because these are independent choices, not a single value. Use a fieldset when several checkboxes answer one question. A Listbox is a better fit once the list is long enough that a stack of boxes becomes a chore.
Table
Each person row has a checkbox, the header checkbox selects or clears every row, and Sarah starts selected. Keep the selected ids in state, and mark the row when it is in that set, so the highlight and the box agree. Use this for bulk actions on a Table. For a handful of options that are not records, the grouped checkboxes are simpler.
Rtl
The group sets dir to rtl. The checkbox stays on the start side of the row, and the label قبول الشروط follows it, so the line reads correctly without swapping the markup. Direction belongs on the group when this block can sit inside a left-to-right page. Checking the box works the same either way.
FAQ
Should I use a Checkbox or a Switch?
Use a Checkbox for a choice that takes effect when the form is submitted, like accepting terms or picking filters. Use a Switch for a setting that applies the moment it changes, and a Radio Group when only one option can be picked.
How do I control a Checkbox's checked state in React?
Hold the checked state yourself and pass it back on every change.
Pass checked and update it in onCheckedChange, which receives the new boolean. Use defaultChecked when you only need a starting value.
How do I show an indeterminate Checkbox?
Use the indeterminate state on a parent whose children are only partly checked, and compute it from the children yourself.
Pass the indeterminate prop.
How do I build a select-all Checkbox for a table?
Keep the selected row ids in a set. The header checkbox is checked when every row is selected, indeterminate when only some are, and toggles all of them when clicked. The Table demo shows the whole pattern.
How do I add a label to a Checkbox in Base UI?
Put the Checkbox and a FieldLabel in a horizontal Field, so clicking the text toggles the box and gives a larger tap target on phones.
Give the checkbox an id and point the label's htmlFor at it.
Does the Checkbox submit with a native form?
Yes. Give it a name and it submits through a hidden input when checked, with value as the submitted value.
With React Hook Form, TanStack Form or Formisch, connect the field's value to the checked state and its change handler: the forms guide has a Checkbox demo for each.
How do I change the Checkbox size or colors?
Edit the checkbox file in your project's components/ui folder: the box, the check icon and their sizes are classes there. Its states come through as data attributes, listed in the API reference below, so you can style checked, disabled and invalid boxes with Tailwind variants.
API Reference
Checkbox
<span> data-slot="checkbox"
checked?boolean
Whether the checkbox is currently ticked.
To render an uncontrolled checkbox, use the defaultChecked prop instead.
- Default
undefined
defaultChecked?boolean
Whether the checkbox is initially ticked.
To render a controlled checkbox, use the checked prop instead.
- Default
false
onCheckedChange?(checked: boolean, eventDetails: CheckboxRootChangeEventDetails) → void
Event handler called when the checkbox is ticked or unticked.
- Type
(checked: boolean, eventDetails: CheckboxRootChangeEventDetails) → void- Parameters
checked– booleaneventDetails– CheckboxRootChangeEventDetails- Returns
void
disabled?boolean
Whether the component should ignore user interaction.
- Default
false
form?string
Identifies the form that owns the hidden input. Useful when the checkbox is rendered outside the form.
id?string
The id of the input element.
indeterminate?boolean
Whether the checkbox is in a mixed state: neither ticked, nor unticked.
- Default
false
inputRef?React.Ref<HTMLInputElement>
A ref to access the hidden <input> element.
- Type
React.Ref<HTMLInputElement>
name?string
Identifies the field when a form is submitted.
- Default
undefined
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to true if the rendered element is a native button.
- Default
false
parent?boolean
Whether the checkbox controls a group of child checkboxes. Must be used in a [Checkbox Group](https://base-ui.com/react/components/checkbox-group).
- Default
false
readOnly?boolean
Whether the user should be unable to tick or untick the checkbox.
- Default
false
required?boolean
Whether the user must tick the checkbox before submitting a form.
- Default
false
uncheckedValue?string
The value submitted with the form when the checkbox is unchecked. By default, unchecked checkboxes do not submit any value, matching native checkbox behavior.
value?string
The checkbox's value. Identifies it within a [Checkbox Group](https://base-ui.com/react/components/checkbox-group), falling back to name when omitted.
When submitting a form, a checked box submits value; with no value, it submits the native "on".
- Default
"on"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
CheckboxIndicator
<span> data-slot="checkbox-indicator"
keepMounted?boolean
Whether to keep the element in the DOM when the checkbox is not checked.
- Default
false
With other components
- Free
Field
InputsLabel and helper text
- Free
Label
InputsClick the words
- Free
Table
DataSelect rows in bulk
- Free
Card
LayoutTerms on a card
- Free
Item
LayoutRow in a list
- Free
Button
ButtonsSubmit the selection
Related
- Free
Switch
SelectionInstant on or off
- Free
Radio Group
SelectionOnly one choice
- FreeBi
Toggle
SelectionPressed in a toolbar
- Free▼
Dropdown Menu
MenusChecks in a menu
- Free✓✓✓✓
Listbox
SelectionMany rows, one widget
- Free××××
Tags Input
InputsChosen values as chips