Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Todo Style Checkbox for React

A checkbox labeled Complete the project documentation. Checking it strikes those words through and mutes them.

A checkbox labeled Complete the project documentation. It starts unchecked. Checking it strikes the words through and mutes them.

Use it for a checklist where finished tasks should stay on the list. A Toggle only shows pressed or not, with no sentence to strike. In React, Base UI does not restyle the label.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

Checkbox anatomy: root, control, indicator, label

Usage

<Field className="w-fit" orientation="horizontal">
  <Checkbox
    checked={checked}
    id="todo"
    name="todo"
    onCheckedChange={handleCheckedChange}
  />
  <FieldLabel
    className={cn(
      "transition-colors",
      checked && "text-muted-foreground line-through"
    )}
    htmlFor="todo"
  >
    Complete the project documentation
  </FieldLabel>
</Field>

FAQ

How does the strikethrough styling work?

The checked value lives in state, and cn() adds line-through text-muted-foreground to the FieldLabel when it's true. Ticking the box updates the state, and the label restyles on the next render. The state is updated in onCheckedChange.

Can I customize the completion animation?

transition-colors fades the color; change the speed with duration-300 or duration-75. The line itself can't animate with a plain class, so keep the effect short: completion should feel instant.

Does the task text stay when the box is checked?

Yes. The label stays in place. While checked is true, line-through and text-muted-foreground apply. Clearing the box drops those classes, because cn() adds them only for that state.

Why is this checkbox controlled?

The label has to read the same value as the box. This demo keeps checked in state, starts it at false, and passes it through. An uncontrolled box would update itself and the label would not know.

How do I start with the task already done?

Start the state at true and the task renders already struck through. The checkbox stays controlled, so the label and the box share that one value. Pass checked.

Are todo checkboxes accessible with strikethrough?

Yes. The strikethrough is only visual; screen readers announce the label and whether the box is checked, which already says the task is done. Keep the text in place rather than swapping it for an icon, and Space toggles the box from the keyboard.

API Reference

Field

Sets orientation="horizontal" and htmlFor="todo". The rest is on the Field page.

Checkbox

Sets checked={checked}, id="todo", name="todo" and onCheckedChange={handleCheckedChange}. The rest is on the Checkbox page.