Skip to content
Components

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

A summary of your team's activity, every Monday.

Props

  • checked
  • defaultChecked
  • disabled
  • label
  • description
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.

  • A summary of your team's activity, every Monday.

    falseOffdefault

  • A summary of your team's activity, every Monday.

    trueOn

  • A summary of your team's activity, every Monday.

    indeterminateMixed

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.

A summary of your team's activity, every Monday.

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

Your admin turned this on for everyone.

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

NameTypeValuesDefaultDescription
labelnode--The visible name, after the box. A press on it toggles the box. Short, sentence case: Email me product updates.
descriptionnode--A second line under the label: what turning it on does. Needs label.
checkedenumfalse | true | indeterminatefalseControlled state: true, false or "indeterminate" (partly on). Pair it with onCheckedChange.
defaultCheckedboolean-falseOn at first render, when you don't control it. Also takes "indeterminate".
onCheckedChangefunction--Called with true or false when the user toggles the box.
disabledboolean-falseCan't be focused or toggled. Disable its Label too.
requiredboolean-falseMust be on to submit the form, such as accepting the terms. Mark its Label required too.
namestring--Form name. The form submits value under this name while the box is on.
valuestring-onValue submitted while the box is on.

Anatomy

PartDescription
rootThe box: 16px, radius-inner, control border on the sunken fill. On and partly on, the accent fills it to the edge.
iconThe check (on) or the minus (partly on): Tabler, 16px, in the on-accent icon color.
hitAreaTransparent ::before centered on the box, never smaller than size-target-min (24px on mouse, 44px on touch).
labelWith label: a Label after the box, level with the box on its first line. It follows required and disabled.
descriptionWith description: 13px secondary text under the label, gap-xs.
rowWith label: the row that holds the box, then the Label and the description, gap-sm. RadioGroup and Switch share it.

Accessibility

Role: checkbox

KeysAction
SpaceTurns the box on or off. A partly on box turns on.
TabMoves 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.json

This 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").