Skip to content

map()

The map() function renders a reactive array as a list of elements.

Loading sandbox...
// Primitives (auto-keyed by value)
map(items, render)
// Objects (explicit key function)
map(items, keyFn, render)

items : A reactive array (signal or computed).

keyFn (required for objects) : A function that returns a unique identifier for each item.

render : A function that returns an element spec for each item. Receives a signal wrapping the item.

For arrays of strings or numbers, the value itself is used as the key:

const items = signal(['Apple', 'Banana', 'Cherry']);
el('ul')(
map(items, (item) => el('li')(item))
)

For objects, provide a key function to track identity:

const todos = signal([
{ id: 1, text: 'Learn', done: false },
{ id: 2, text: 'Build', done: false },
]);
el('ul')(
map(
todos,
(todo) => todo.id, // key function
(todo) => el('li')( // render function
computed(() => todo().text)
)
)
)

The render function receives a signal wrapping each item, not the plain value. This enables reactive updates when items change:

map(todos, t => t.id, (todo) => {
// todo is a signal, read it to get the value
return el('li')(
computed(() => todo().text),
computed(() => todo().done ? '' : '')
);
})

When you update an item in the source array, the corresponding item signal updates — the element isn’t recreated:

// Update one todo — the element updates reactively
todos(arr => arr.map(t =>
t.id === 1 ? { ...t, done: true } : t
));
const names = signal(['Alice', 'Bob', 'Carol']);
el('ul')(
map(names, (name) => el('li')(name))
)
// Add item
names([...names(), 'Dave']);
// Remove item
names(names().filter(x => x !== 'Bob'));
type Todo = { id: string; text: string; done: boolean };
const todos = signal<Todo[]>([]);
const toggle = (id: string) => {
todos(todos().map(t =>
t.id === id ? { ...t, done: !t.done } : t
));
};
const remove = (id: string) => {
todos(todos().filter(t => t.id !== id));
};
el('ul')(
map(todos, t => t.id, (todo) =>
el('li').props({
style: computed(() =>
todo().done ? 'text-decoration: line-through' : ''
),
})(
el('input').props({
type: 'checkbox',
checked: computed(() => todo().done),
onchange: () => toggle(todo().id),
})(),
computed(() => todo().text),
el('button').props({
onclick: () => remove(todo().id)
})('×')
)
)
)
type Category = { id: string; name: string; items: string[] };
const categories = signal<Category[]>([...]);
el('div')(
map(categories, c => c.id, (category) =>
el('section')(
el('h2')(computed(() => category().name)),
el('ul')(
map(
computed(() => category().items),
(item) => el('li')(item)
)
)
)
)
)

If you need the index, derive it from the array position:

const items = signal(['A', 'B', 'C']);
el('ol')(
map(items, (item, index) =>
el('li')(computed(() => `${index() + 1}. ${item()}`))
)
)
  • el() — Create elements
  • match() — Conditional rendering
  • iter() — The data structure powering map()
  • Signal Patterns — Patterns for arrays and collections