npx shadcn@latest add https://sdk-components.thesqd.com/r/todo-list.jsonimport { TodoList, type TodoItem } from "@/components/blocks/todo-list";"use client";
import { TodoList } from "@/components/blocks/todo-list";
export function Example() {
return (
<TodoList
title="Tasks"
defaultItems={[{ id: "1", label: "Call the venue" }]}
onChange={(items) => console.log(items)}
/>
);
}TodoList (single component — pass everything as props)
└── Card type="toolbar" collapsible
├── band (records-toolbar — click to fold the list)
│ ├── icon chip (records-database-icon; duotone ListChecks by default)
│ ├── title
│ ├── CircularProgress (done / total ring — hidden until the first tick)
│ ├── Badge ("1/5" NumberReel — coral < 40%, amber 40%+,
│ │ green at 100%)
│ └── CaretDown (flips when open)
└── CollapsibleContent
├── emptyState (no items — Empty with Inky by default)
├── item[]
│ ├── Checkbox (round, sm — strikes the label when ticked;
│ │ Check / Minus icons when readOnly)
│ ├── label (one of three:)
│ │ ├── <a> (item has an href — the label is the link)
│ │ ├── InvisibleInput (rename in place; Backspace on empty removes)
│ │ └── <span> (readOnly)
│ ├── Badge[] (item.badge — one or a list, in order, right-aligned)
│ └── Button × ghost (remove, shown on hover/focus)
└── add row
├── Plus
└── InvisibleInput (Enter or blur adds)Add a task below to get started.
Enjoy the party — or add one more thing.
4 left — state lives in the parent.
onItemChange
Use the buttons or the list.
TodoList
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | "To do" | Header label. |
header | "band" | "label" | "band" | `band` is the shaded toolbar band; `label` is the project viewer's quiet header — a small muted name beside a brand-coloured icon. Both fold the list. |
fill | boolean | false | Fill the height and width the parent gives it — a grid cell, a dashboard widget — instead of sizing to the tasks; the list scrolls inside. Folded, it still shrinks to its header. |
icon | ComponentType<{ className?: string }> | false | ListChecks (duotone) | Icon component for the chip beside the title. `false` hides it. |
items | TodoItem[] | — | Controlled items — pair with `onChange`. |
defaultItems | TodoItem[] | [] | Starting items when uncontrolled. |
onChange | (items: TodoItem[]) => void | — | Fires with the whole new list on every tick, rename, add and remove. |
onItemChange | (change: TodoChange) => void | — | Fires once per item that changed — add, update or remove — with the item (and `previous` on update), so a save can PATCH one record. |
empty | Pick<EmptyProps, "inky" | "title" | "description" | "action"> | — | Tunes the default empty state — Inky as an emotion or any Inky props (`{ costume, preset, shape, … }`), plus title, description and an action. Unset fields keep their defaults. |
emptyState | ReactNode | — | Replaces the empty state outright with your own node, or `null` for none. Wins over `empty`. |
placeholder | string | "Add a task" | Placeholder of the add row. |
readOnly | boolean | false | Display only — no ticking, renaming, adding or removing. Links still work, and the ref API still changes it. |
defaultOpen | boolean | true | Whether the list starts unfolded. |
collapsible | boolean | true | Whether the header folds the list away (caret on the right). `false` draws a plain header that always shows the list — for a board where folding is not wanted. |
iconTone | "muted" | "coral" | — | The header icon's tone, passed to the Card: `coral` while something waits, `muted` for an empty list. |
sound | boolean | true | Cuelume cues on ticks — `select` on check, `error` on uncheck, `success` instead of `select` when the tick finishes the list. Ref API ticks play them too. |
className | string | — | Override the card's width or layout classes. |
ref | Ref<TodoListHandle> | — | Drive the list from code — see TodoListHandle. |
TodoItem
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Stable key. |
label | string | — | The task text. |
done | boolean | false | Ticked. |
href | string | — | Makes the label a link. New-tab links get the global ↗ after the text. Linked labels aren't renamed inline — use the ref's `update`. |
badge | TodoBadge | TodoBadge[] | — | Extra metadata — an owner, a due date, a department — as small badges right-aligned on the row. One badge or a list, drawn in the order given. `TodoBadge` is `{ label: ReactNode; color?: BadgeColor }`; `muted` by default. |
target | string | "_blank" for http(s), else same tab | Link target. |
TodoListHandle
| Prop | Type | Default | Description |
|---|---|---|---|
getItems | () => TodoItem[] | — | The current list. |
setItems | (items: TodoItem[]) => void | — | Replaces the whole list. |
add | (item: Omit<TodoItem, "id"> & { id?: string }) => string | — | Appends an item; returns its id. |
update | (id: string, patch: Partial<TodoItem> | (item: TodoItem) => Partial<TodoItem>) => void | — | Changes any field of one item — label, done, href, target. A function patch derives from the current item. |
toggle | (id: string, done?: boolean) => void | — | Flips `done`, or sets it when passed. |
remove | (id: string) => void | — | Removes one item. |
clearCompleted | () => void | — | Removes every ticked item. |
open / close | () => void | — | Unfolds or folds the list. |
focusAdd | () => void | — | Opens the list and puts the cursor in the add row. |
TodoChange
| Prop | Type | Default | Description |
|---|---|---|---|
type | "add" | "update" | "remove" | — | What happened. |
item | TodoItem | — | The item after the change (or the removed item). |
previous | TodoItem | — | Update only — the item before the change. |