Components / Controls
Checklist
A list of rows people add, tick, rename and remove, such as a task's subtasks. If people only choose among options you wrote, use Checkbox instead.
When to useLink to "When to use"
- A list people write themselves and work through: subtasks, a packing list, the steps of a recipe.
reorderable: when the order matters to them.readOnly: to show the same list where it cannot be changed.
Use something else when:
- the options are fixed and people only tick them: Checkbox
- each row has a name and a value: KeyValueEditor
- the rows are short tags: TagInput
- the rows are only put in order: SortableList
- the rows are steps of a process someone else defined: Steps
UsageLink to "Usage"
import { Checklist, type ChecklistItem } from "@arshad-shah/cynosure";
const [subtasks, setSubtasks] = useState<ChecklistItem[]>([]);
<Checklist label="Subtasks" items={subtasks} onChange={setSubtasks} />
<Checklist label="Packing list" defaultItems={[]} onChange={save} />Each row is { id, label, done }. Give rows from your data a stable id; rows added in the list get one from createId (by default crypto.randomUUID(), or a time-based id where that is missing). onChange receives the full list after every change.
Renaming a rowLink to "Renaming a row"
The Rename button turns the name into a field with the old name selected. Enter saves, Escape cancels, and leaving the field saves. A blank name is not saved: the row keeps the name it had, and Remove is the only way to delete a row.
ReorderingLink to "Reordering"
reorderable puts the rows in a SortableList: each gets a grip to drag, or to move with Space and the arrow keys.
Limits and statesLink to "Limits and states"
maxItems turns the add field and button off when the list is full. disabled turns everything off. readOnly shows only the rows.
PropsLink to "Props"
| Prop | What it does | Type | Default |
|---|---|---|---|
labelRequired | What the rows are ("Subtasks"). Names the list; shown above it unless labelHidden. | string | |
labelHidden | Visually hide the label (it still names the list). Default false. | boolean | false |
items | The rows, when you control them: { id, label, done }. | ChecklistItem[] | |
defaultItems | Starting rows when uncontrolled. | ChecklistItem[] | |
onChange | Called with the full list after each change: a row added, ticked, renamed, removed or moved. | ((items: ChecklistItem[]) => void) | |
createId | Makes the id of a new row. Default: crypto.randomUUID(), or a time-based id where that is missing. | (() => string) | `crypto |
maxItems | The most rows the list may hold; the add field turns off when it is full. | number | |
reorderable | Rows get a grip to drag, or to move with Space and the arrow keys (SortableList). Default false. | boolean | false |
disabled | Turns off every checkbox, field and button. Default false. | boolean | false |
readOnly | Shows the rows as they are: nothing can be ticked, added, renamed, removed or moved. Default false. | boolean | false |
size | Size of the checkboxes, text, fields and buttons. Default "md". | "sm" | "md" | "md" |
labels | Replace the built-in English text. | Partial<ChecklistLabels> | |
className | Added to the root element, for layout. | string | |
style | Layout only. Restyle through --cy-checklist-* tokens. | CSSProperties |
Generated from the type definitions in @arshad-shah/cynosure 0.5.0.
Behaviour and accessibilityLink to "Behaviour and accessibility"
| Input | Result |
|---|---|
| Enter in the add field, or the Add button | Adds the trimmed name as a new row and keeps focus in the field; a blank name adds nothing |
| Click, tap or Space on a row's checkbox or name | Ticks or unticks it |
| Rename button (click, tap, Enter, Space) | Opens the name in a field, focused, with the old name selected |
| Enter in the rename field | Saves and returns focus to the Rename button |
| Escape in the rename field | Cancels and returns focus to the Rename button |
| Tab or a press outside the rename field | Saves and leaves focus where it went |
| Remove button | Deletes the row and focuses the checkbox of the row that took its place, or the row before it, or the add field when none is left |
Grip (reorderable): drag, or Space, arrows, Space | Moves the row; SortableList announces each step |
- Names: the group and the list are named by
label. Each checkbox is named by its row, and the buttons read "Rename Buy milk" and "Remove Buy milk". - Done rows are struck through and muted by Checkbox (
strikeWhenChecked). - Full list: adding the row that reaches
maxItemsmoves focus to that row's checkbox, because the add field turns off. - Forms and dialogs: Enter in either field does not submit a form around the list, and Enter and Escape in the rename field do not reach a dialog around it. Enter is left alone while an input method is composing.
- Read only: the checkboxes can still be focused and read, and a press leaves them as they were.
- Announcements: adding, renaming and removing are not announced; focus moves to a named control instead.
- Right to left: the row mirrors.
TouchLink to "Touch"
The Rename, Remove and Add buttons and the grip have 44px hit areas on coarse pointers (phones and tablets), and each checkbox row is at least 44px tall. Nothing depends on a double click or on hover.
ThemingLink to "Theming"
Tokens: --cy-checklist-gap (space between rows, and between the rows and the add field) and --cy-checklist-row-gap (space between a row's checkbox, name and buttons). The checkbox, the strike, the fields, the buttons and the reorderable rows follow the Checkbox, TextField, Button and SortableList tokens.
createTheme({ components: { checklist: { gap: "0.5rem" } } });className and style are for layout only (margin, placement, width). Change the look through tokens so every product stays consistent.
RelatedLink to "Related"
Checkbox · KeyValueEditor · SortableList · TagInput · TextField
These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.