# Checkbox

> A real checkbox whose fill grows from the centre and whose tick draws itself, with a mixed state that bends into the tick.

Custom checkboxes often drop the native input and lose forms, labels and Space. This one keeps a real input underneath; on top, the box fills from its centre with a slight overshoot and the tick draws along its stroke, retracting the way it came. A mixed dash grows from the middle for "some selected".

## Install

```bash
npx shadcn@latest add https://hairlineui.com/r/checkbox.json
```

Needs React 19, Tailwind CSS v4 and shadcn/ui theme variables (any style, light and dark). Icons from lucide-react.

- npm: none
- Hairline UI components it needs (the CLI installs them too): `ui-interaction-motion`
- Docs and live preview: https://hairlineui.com/ui/checkbox
- Registry item (source): https://hairlineui.com/r/checkbox.json

## Usage

```tsx
import { Checkbox } from "@/components/ui/checkbox";

<Checkbox label="Email me about new templates" description="About once a month." defaultChecked />
<Checkbox checked={some ? "mixed" : all} onCheckedChange={selectAll} aria-label="Select all" />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `checked` | `boolean \| "mixed"` | – | Controlled state. |
| `defaultChecked` | `boolean` | `false` | Initial state when uncontrolled. |
| `onCheckedChange` | `(checked: boolean) => void` | – | Called with the new state. |
| `label / description` | `ReactNode` | – | Text beside the box. |
| `size` | `"sm" \| "md"` | `"md"` | 14px or 16px box. |
| `...input props` | `InputHTMLAttributes` | – | name, value, required, disabled, ref. |

## States

- **unchecked**: A hairline box; hover darkens the line.
- **checked**: Primary fill grows from 0.4 with a slight overshoot (320ms) and the tick draws (300ms, 90ms later).
- **mixed**: A dash grows from the centre (260ms).
- **focus**: The ring and a 3px halo, from the keyboard only.

## What it handles

Rebuilding a checkbox from divs means rebuilding forms, labels, Space and screen-reader state. This keeps the native input and only draws over it.

- **A tick that draws**: pathLength stroke, 300ms after the fill lands; it retracts on uncheck.
- **Mixed state**: checked="mixed" sets indeterminate on the input and grows a dash.
- **Native underneath**: Forms, labels, Space and aria-checked behave as always.
- **Press feedback**: The box gives under the finger and settles back.

## Replaces

custom checkbox, indeterminate wiring, tick animation

## Where it fits

- **Settings and consent**: Labelled options with a second line of description.
- **Table row selection**: A header box that shows "some selected" as a dash.
- **Task and to-do lists**: Ticks that draw as you finish things.
- **Sign-up terms**: A required box inside a native form.

## Notes

- Reduced motion: the fill and tick change at once.
- The whole label is the hit area.

License: MIT (https://hairlineui.com/license#components)
