Checkbox
A 16px box that turns one option on or off, or shows that a group is partly on. Sunken with the control border when off; the accent with a check when on, or a minus when partly on. label puts its name after it, and description a second line under the name.
Preview
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="Weekly digest" description="A summary of your team's activity, every Monday." />Usage
When to use
- Turn one option on or off when the change takes effect on save, such as accepting the terms.
- Pick any number of items from a list, such as rows in a table, each with its own box.
When not to use
- A setting that applies at once, such as dark mode. Use Switch.
- Picking exactly one of several options. Use RadioGroup, or Select for a long list.
- Running an action, such as deleting the selected rows. Use Button.
Variants
checked
Controlled state: true, false or "indeterminate" (partly on). Pair it with onCheckedChange.
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="Weekly digest" description="A summary of your team's activity, every Monday." />
<Checkbox label="Weekly digest" description="A summary of your team's activity, every Monday." defaultChecked />
<Checkbox label="Weekly digest" description="A summary of your team's activity, every Monday." defaultChecked="indeterminate" />Examples
Default
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="Email me product updates" />With a description
The description is read after the name.
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="Weekly digest" description="A summary of your team's activity, every Monday." defaultChecked />Required
The form can't be sent until the box is on. The asterisk is on the label; required on the box is what screen readers announce.
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="I accept the terms" required />Partly on
A box that selects every row, while only some rows are selected. The table shows the name, so the box takes aria-label.
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox defaultChecked="indeterminate" aria-label="Select all rows" />Disabled
import { Checkbox } from "@/components/ui/checkbox"
<Checkbox label="Sign in with SSO" description="Your admin turned this on for everyone." disabled defaultChecked />Do and don't
Do
- Name it with the label prop; add a description when the name alone doesn't say what turning it on does.
- Use it for a choice that takes effect when the form is saved, or to pick several items.
- Use partly on for a box that selects a list of which only some items are on.
Don't
- Don't use it for a setting that applies at once; use Switch.
- Don't use it to pick exactly one of several options; use RadioGroup.
- Don't put the name before the box, or the description inside the label.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| label | node | - | - | The visible name, after the box. A press on it toggles the box. Short, sentence case: Email me product updates. |
| description | node | - | - | A second line under the label: what turning it on does. Needs label. |
| checked | enum | false | true | indeterminate | false | Controlled state: true, false or "indeterminate" (partly on). Pair it with onCheckedChange. |
| defaultChecked | boolean | - | false | On at first render, when you don't control it. Also takes "indeterminate". |
| onCheckedChange | function | - | - | Called with true or false when the user toggles the box. |
| disabled | boolean | - | false | Can't be focused or toggled. Disable its Label too. |
| required | boolean | - | false | Must be on to submit the form, such as accepting the terms. Mark its Label required too. |
| name | string | - | - | Form name. The form submits value under this name while the box is on. |
| value | string | - | on | Value submitted while the box is on. |
Anatomy
| Part | Description |
|---|---|
| root | The box: 16px, radius-inner, control border on the sunken fill. On and partly on, the accent fills it to the edge. |
| icon | The check (on) or the minus (partly on): Tabler, 16px, in the on-accent icon color. |
| hitArea | Transparent ::before centered on the box, never smaller than size-target-min (24px on mouse, 44px on touch). |
| label | With label: a Label after the box, level with the box on its first line. It follows required and disabled. |
| description | With description: 13px secondary text under the label, gap-xs. |
| row | With label: the row that holds the box, then the Label and the description, gap-sm. RadioGroup and Switch share it. |
Accessibility
Role: checkbox
| Keys | Action |
|---|---|
| Space | Turns the box on or off. A partly on box turns on. |
| Tab | Moves focus to the next box; each checkbox is its own tab stop. |
- Name every box: the label prop (a press on it toggles the box), or aria-label when the name is shown elsewhere, such as a row in a table. A separate Label with htmlFor also works when the layout needs it.
- description is linked with aria-describedby and sits outside the label element, so screen readers read it after the name instead of as part of it.
- Partly on is aria-checked="mixed", for a box that selects a list of which only some items are on.
- Enter does not toggle a checkbox, as in a native one: it submits the form.
- The box is 16px but its hit area is size-target-min (24px on mouse, 44px on touch).
- Inside a form, a hidden input carries name and value, so the form submits it like a native checkbox.
- On and off differ by the check or minus as well as the fill, so the state never relies on color alone.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/checkbox.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").