2. Lists and bindings
A count isn't a reading list. In this step the page keeps real books in an array, renders one row per book, and lets you tick them off.
A list of books
Replace src/routes/index.tsx again:
interface Book {
id: number;
title: string;
read: boolean;
}
let books = $state<Book[]>([
{ id: 1, title: "The Left Hand of Darkness", read: true },
{ id: 2, title: "Piranesi", read: false },
]);
let title = $state("");
let nextId = 3;
let unread = $derived(books.filter((b) => !b.read).length);
function add(e: SubmitEvent) {
e.preventDefault();
if (!title.trim()) return;
books.push({ id: nextId++, title: title.trim(), read: false });
title = "";
}
const Index = () => (
<main>
<h1>Reading list</h1>
<form onSubmit={add}>
<input placeholder="Book title" bind:value={title} />
<button>Add</button>
</form>
<ul>
{books.map((book) => (
<li key={book.id}>
<label>
<input type="checkbox" bind:checked={book.read} />
{book.title}
</label>
</li>
))}
</ul>
<p>{unread} still to read.</p>
</main>
);
export default Index;Add a book with the form (Enter works too), and tick a box: the "still to read" count follows both.
Arrays and objects in state
$state wraps objects and arrays in a proxy that tracks each property separately. That's why books.push(…) updates the list: you don't need to build a new array. It's also why ticking a box works: book.read is a property of an object inside books, so changing it updates the row, and unread, which reads read on every book.
nextId is a plain let. Nothing on the page shows it, so it doesn't need to be reactive.
Keys
books.map(…) renders one <li> per book. The key tells Sigil which row belongs to which book. When the array changes, rows are matched by key: a new book gets a new row, a removed book loses its row, and a reordered list moves the existing rows instead of rebuilding them.
Use a stable id from your data as the key, not the array index.
Bindings
bind:value={title} replaces the value + onInput pair from step 1. It writes the input into title as you type and writes title back into the input when it changes, which is how title = "" clears the box.
bind:checked={book.read} does the same for a checkbox, and it works on a property: ticking the box sets book.read on that book.
The JSX reference lists every binding, including bind:group for radio buttons and bind:this for getting hold of an element.
Next: components and styles.