# Checkbox

> A React checkbox that is on, off or mixed, with a check that draws itself, full-row labels, cards, and groups with select all.

- Type: React component (Inputs)
- Access: Free (the source is open to everyone)
- Version: 1.0.1
- Install: `npx wingo-ui@latest add checkbox`
- Docs: https://wingo-ui.com/components/checkbox

## Interaction

Click the box, its label or its description and the check draws itself on a quick spring; uncheck it and the stroke pulls back faster. While you press, the box dips a little, and with a mouse its border darkens on hover. Tab reaches every box and Space toggles it (Enter does not, as the ARIA pattern says); an indeterminate box turns checked on the next press. A link inside the label opens without toggling. Press Place order with the terms unchecked and the error slides in under the row; checking the box clears it. Cards take a ring in the tone when checked and dip slightly while pressed. In a group, Select all checks every enabled option and shows a dash while only some are checked (it turns checked once every enabled option is); with a maximum, the remaining options disable once you reach it. On a phone every row is at least 44px tall and the whole row is the target, and with reduced motion the check appears without drawing and nothing dips.

## Installation

```bash
npx wingo-ui@latest add checkbox
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/checkbox.json`

Files:

- `components/ui/checkbox.tsx`

npm dependencies: `npm i @radix-ui/react-checkbox motion`

Wingo UI dependencies (installed automatically):

- [Field](https://wingo-ui.com/components/field) ([Markdown](https://wingo-ui.com/components/field.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { useState } from "react"
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox"

const AMENITIES = [
  { value: "parking", label: "Parking" },
  { value: "balcony", label: "Balcony" },
  { value: "elevator", label: "Elevator" },
  { value: "laundry", label: "In-unit laundry" },
]

export function OrderConsent({ showError }: { showError: boolean }) {
  const [accepted, setAccepted] = useState(false)
  const [amenities, setAmenities] = useState<string[]>(["parking"])
  return (
    <form className="flex flex-col gap-4">
      <Checkbox
        name="terms"
        required
        checked={accepted}
        onCheckedChange={(checked) => setAccepted(checked === true)}
        label="I agree to the Terms and conditions"
        error={showError && !accepted ? "You need to accept the terms." : undefined}
      />
      <CheckboxGroup
        label="Amenities"
        name="amenities"
        columns={2}
        options={AMENITIES}
        value={amenities}
        onValueChange={setAmenities}
      />
    </form>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label?` | `ReactNode` | `""` | Text beside the box. The whole row is one <label>, so clicking the text, the description or the empty space toggles the box. A link inside it opens without toggling. |
| `description?` | `ReactNode` | `""` | A second line under the label, in the muted caption style. It is kept out of the box's name and linked with aria-describedby, so screen readers read it after the label. |
| `error?` | `ReactNode` | `""` | A message under the row, lined up with the label, that slides in and marks the box invalid (red border, aria-invalid). "", false and null are no error. A box without a label only turns red; wrap it in a Field to show the text. |
| `required?` | `boolean` | `false` | Shows the red mark after the label, sets aria-required, and inside a native form blocks submission while unchecked (through Radix's hidden input). |
| `invalid?` | `boolean` | `false` | The red border and aria-invalid without a message, for forms that show errors elsewhere. Inherits from a parent Field. |
| `disabled?` | `boolean` | `false` | Dims the row to 50% and blocks it. A disabled Field or FieldGroup around it disables it too. |
| `readOnly?` | `boolean` | `false` | Shows the value but ignores clicks and Space, with no hover or press feedback; the box keeps focus and sets aria-readonly. |
| `defaultChecked?` | `boolean \| "indeterminate"` | `false` | The starting state while uncontrolled: true, false or "indeterminate" (a dash, aria-checked="mixed"). |
| `checked?` | `boolean \| "indeterminate"` |  | The state while controlled. Pair it with onCheckedChange. |
| `onCheckedChange?` | `(checked: boolean \| "indeterminate") => void` |  | Called with the next state. Clicking an indeterminate box makes it true, like Radix. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Box 16 / 18 / 20px with a 12 / 14 / 16px check and a 13 / 14 / 15px label. The box centers on the label's first line at every size. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The checked fill (and the card ring and icon tile). neutral is black in light mode and white in dark mode; keep brand for the one place it means something. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone; the check turns black or white by the color's lightness. Leave empty to use the tone. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Box corners, scaled with the size: md is 4 / 5 / 6px, full makes a round checkbox. |
| `variant?` | `"default" \| "card"` | `"default"` | card turns the row into a soft-filled tile with the box at the top end, an optional icon tile, and a ring in the tone when checked. For rich choices like paid extras. |
| `labelPosition?` | `"end" \| "start"` | `"end"` | start puts the label first and the box at the end of a full-width row, like a settings list. Cards ignore it. |
| `icon?` | `ReactNode` |  | Cards: the leading icon in a 40px tile that takes the tone when checked. Rows: a thumbnail or avatar between the box and the text (the row then centers). |
| `name?` | `string` | `""` | Form field name. Inside a <form> Radix renders a hidden input that submits value while checked and returns to its first state when the form resets. The playground sits in a real form. |
| `value?` | `string` | `"on"` | What the form submits while the box is checked. |
| `id?` | `string` |  | The box's id. Defaults to useId(); inside a Field the Field's id wins, so its label toggles the box. |
| `classNames?` | `Partial<Record<"field" \| "box" \| "indicator" \| "content" \| "label" \| "description" \| "error" \| "icon", string>>` |  | Extra classes for the parts: field is the label row (or the card), box the button, indicator the check svg. Padding for list rows goes on field so the padding stays clickable. |
| `className?` | `string` |  | Classes for the outermost element: the wrapper around the row and its message, or the box itself when there is no label. ref and every other prop (aria-*, data-*, onBlur, form) go to the box, a <button role="checkbox">, so form libraries can focus it. |

## Parts

### CheckboxGroup (component)

A list of options with one label (role="group"). options: { value, label, description?, icon?, disabled? }[]; value / defaultValue / onValueChange (string[], kept in the options' order); label, description, error, required, invalid, disabled, readOnly; orientation ("vertical" | "horizontal", rows wrap); columns (1 | 2 | 3 | 4, one on phones); variant, size, tone, color, radius, labelPosition for every box; selectAll and selectAllLabel ("Select all"); max (0 = no limit) with maxLabel (n => `Choose up to ${n}`); name (every checked box submits its value, FormData.getAll(name)); fieldProps; classNames (group, all, option, box, indicator, icon, content, optionLabel, optionDescription plus the Field parts). Props: CheckboxGroupProps.

### CheckboxIndicator (component)

The box alone, aria-hidden, with the same look and animation but no behavior: checked, size, tone, color, radius, invalid, disabled. For listbox rows (role="option" must not hold a real checkbox) and table cells that own the state. Props: CheckboxIndicatorProps.

### CheckboxOption (type)

One option of CheckboxGroup: { value, label, description?, icon?, disabled? }.

## Examples

- **Group in two columns**: CheckboxGroup with a label and a name: every checked box submits its value, shown below as form data.
- **Cards with prices**: variant="card" with icon tiles in the brand tone. The ring and the tile take the tone; the total follows in dollars.
- **Select all**: selectAll adds a row that shows a dash while some are checked. Rows with a thumbnail center; delete the selection to see the list update.
- **Maximum choices**: max={3}: once three are checked the rest disable, and maxLabel explains why.
- **Sizes, tones and radius**: 16, 18 and 20px boxes, every tone, a custom color, round and square corners, and the aria-hidden CheckboxIndicator for listbox rows.
- **Settings list**: labelPosition="start" rows inside a list group: the whole row is the target, padding goes on classNames.field.
- **Disabled, read-only and invalid**: Dimmed, locked and red states; the last one shows its error until you check it.

## See also

- [Field](https://wingo-ui.com/components/field) ([Markdown](https://wingo-ui.com/components/field.md))
- [Radio Group](https://wingo-ui.com/components/radio-group) ([Markdown](https://wingo-ui.com/components/radio-group.md))
- [Switch](https://wingo-ui.com/components/switch) ([Markdown](https://wingo-ui.com/components/switch.md))
- [Toggle](https://wingo-ui.com/components/toggle) ([Markdown](https://wingo-ui.com/components/toggle.md))
- [Card](https://wingo-ui.com/components/card) ([Markdown](https://wingo-ui.com/components/card.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))

---

Canonical: https://wingo-ui.com/components/checkbox
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
