Skip to content
Whisq v0.1.0-alpha.9

List Rendering

import { signal, ul, li } from "@whisq/core";
const items = signal(["Apple", "Banana", "Cherry"]);
ul(
() => items.value.map(item => li(item))
)

For large or frequently-changing lists, each() with a key function reuses DOM nodes. In keyed mode the render callback receives hybrid accessors (ItemAccessor<T>) — read via todo.value (canonical since alpha.8), todo() (legacy call form), or todo.peek() (read without subscribing). All three return the current item. See /api/each/ for the full type and four-archetype reactivity rule.

import { signal, each, ul, li, span, button } from "@whisq/core";
const todos = signal([
{ id: 1, text: "Learn Whisq" },
{ id: 2, text: "Build an app" },
]);
ul(
each(
() => todos.value,
(todo) =>
li(
span(todo.value.text), // snapshot read
button({ onclick: () => remove(todo.value.id) }, "x"), // fresh read at click
),
{ key: (t /* value, not accessor */) => t.id },
),
)

Non-keyed each() is unchanged: the callback still receives (item: T, index: number). The accessor shape only applies when you pass { key }. The key: callback receives the value (not the accessor) — same in both shapes.

ApproachUse When
.map()Small lists, rarely changing
each() without keyMedium lists, index identity
each() with keyLarge lists, reorderable items
  • Lifecycle — Hooks for mount, cleanup, async data
  • Nested Item Editing — three canonical patterns for editing a field on one row (decision matrix + worked examples)

Docs current to v0.1.0-alpha.9 . All releases →