Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Lists: For and Index

To render an array — for example, a task list — Solid uses the built-in For component. It creates markup for every element, and when the array changes it adds, removes or moves only the affected nodes. Unlike React, you do not specify keys.

For

// src/components/TodoList.jsx
import { createSignal, For } from 'solid-js';
 
export default function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, title: 'Buy milk' },
    { id: 2, title: 'Call Oleg' },
  ]);
 
  const add = () => setTodos([...todos(), { id: Date.now(), title: 'New task' }]);
 
  return (
    <div>
      <ul>
        <For each={todos()}>{(todo) => <li>{todo.title}</li>}</For>
      </ul>
      <button onClick={add}>Add</button>
    </div>
  );
}

Render <TodoList /> in src/App.jsx. The page shows a list of two tasks, and each click adds another one. The existing li elements are not recreated.

The element's index

The function receives the element's index as the second argument. It can change when items move, so it is a getter function:

// src/components/TodoList.jsx
import { For } from 'solid-js';
 
export default function TodoList(props) {
  return (
    <ol>
      <For each={props.todos} fallback={<li>No tasks</li>}>
        {(todo, index) => (
          <li>
            {index() + 1}. {todo.title}
          </li>
        )}
      </For>
    </ol>
  );
}

Usage: <TodoList todos={todos()} />. The fallback attribute shows "No tasks" while the array is empty.

Index

Index looks similar, but it ties markup to a position in the array rather than to an element:

// src/components/Scores.jsx
import { createSignal, Index } from 'solid-js';
 
export default function Scores() {
  const [scores, setScores] = createSignal([10, 20, 30]);
 
  return (
    <div>
      <Index each={scores()}>{(score, i) => <p>Place {i + 1}: {score()}</p>}</Index>
      <button onClick={() => setScores([15, 20, 30])}>Update</button>
    </div>
  );
}

After the click only the text of the first paragraph changes. Here it is the other way round: the element is a getter score(), and the index i is a plain number.

Which to choose

ForIndex
Tied toThe element (object)The position
ElementValue todoGetter score()
IndexGetter index()Number i
Good forLists of objects: tasks, postsSimple values: strings, numbers

Most of the time you need For.

Common mistake

Rendering a list with map, as in React:

// src/components/TodoList.jsx
export default function TodoList(props) {
  // Works, but recreates every li on each change
  return <ul>{props.todos.map((todo) => <li>{todo.title}</li>)}</ul>;
}

map creates all elements anew on any change of the array: focus in fields inside the list is lost and extra work is done. Use For.

Thanks for reading!

Donate to the site — we work hard for you.