Lists
For renders one row per item and moves rows instead of rebuilding them.
Show the code
import { useSignal } from "@shadow-js/core";
import { For } from "@shadow-js/web";
const names = ["Ada", "Grace", "Linus", "Margaret", "Alan"];
export function Rows() {
const [rows, setRows] = useSignal(names);
const shuffle = () => setRows([...rows()].sort(() => Math.random() - 0.5));
return (
<div class="stack">
<For each={rows()}>{(name) => <input placeholder={name} aria-label={name} />}</For>
<button type="button" onClick={shuffle}>
Shuffle rows
</button>
</div>
);
}
Type something in a row, then shuffle. The row moves and your text goes with it: For kept each row's DOM and its input, and moved them. It matched rows to items by identity, so the same string is the same row.
Items that are objects#
When your data is replaced by fresh copies, a server response or an immutable update, identity changes on every update and every row would rebuild. Give For a key and rows are matched by that value instead. The row function then receives the item as an accessor:
Show the code
import { useSignal } from "@shadow-js/core";
import { For } from "@shadow-js/web";
interface Todo {
id: number;
title: string;
}
export function Todos() {
const [todos, setTodos] = useSignal<Todo[]>([
{ id: 1, title: "Write a doc" },
{ id: 2, title: "Feed the cache" },
]);
let next = 3;
return (
<div class="stack">
<button type="button" onClick={() => setTodos((list) => [...list, { id: next, title: `Todo ${next++}` }])}>
Add a todo
</button>
<ul>
<For each={todos()} key={(todo) => todo.id}>
{(todo) => (
<li>
{todo().title}{" "}
<button type="button" onClick={() => setTodos((list) => list.filter((t) => t.id !== todo().id))}>
Remove
</button>
</li>
)}
</For>
</ul>
</div>
);
}
| You write | Rows are matched by | The row function gets |
|---|---|---|
<For each={list}> |
The item itself (===) |
The item, and an index accessor |
<For each={list} key={(item) => item.id}> |
key(item) |
An accessor of the item, and an index accessor |
<Index each={list}> |
The position | An accessor of the item, and the index |
Keys must be unique. A repeated key still renders every item, but the later copies get a fresh row on every update, and development builds warn.
Go deeper#
Lists and keys shows how rows move, what happens to focus, empty lists with a fallback, stores with arrays and constant-time selection.