Each

Stage: implementation

NOTE

Each is experimental and currently a technical preview. Its API and behavior may still change as we gather feedback from real-world usage.

Each is a built-in Qwik component for rendering keyed lists.

It is most useful when list items have a stable identity and you want Qwik to preserve and move existing rows instead of re-rendering the whole list when the order changes.

To use it, you must add experimental: ['each'] to your qwikVite plugin options:

// vite.config.ts
import { defineConfig } from 'vite';
import { qwikVite } from '@qwik.dev/core/optimizer';
 
export default defineConfig(() => {
  return {
    plugins: [
      qwikVite({
        experimental: ['each'],
      }),
    ],
  };
});
import { Each, component$, useSignal } from '@qwik.dev/core';
 
type Todo = {
  id: string;
  label: string;
};
 
export default component$(() => {
  const todos = useSignal<Todo[]>([
    { id: '1', label: 'Write docs' },
    { id: '2', label: 'Review keys' },
    { id: '3', label: 'Ship it' },
  ]);
 
  return (
    <>
      <button onClick$={() => (todos.value = [...todos.value].reverse())}>
        Reverse order
      </button>
 
      <ul>
        <Each
          items={todos.value}
          key$={(todo) => todo.id}
          item$={(todo) => <li>{todo.label}</li>}
        />
      </ul>
    </>
  );
});

Props

Each accepts three props:

  • items: the array to render
  • key$: returns a stable unique key for each item
  • item$: returns the JSX for one item

item$ must return a single JSX node. If you need multiple siblings, wrap them in a container element or a Fragment.

import { Each, Fragment, component$ } from '@qwik.dev/core';
 
export default component$(() => {
  return (
    <ul>
      <Each
        items={['a', 'b', 'c']}
        key$={(item) => item}
        item$={(item) => (
          <Fragment>
            <li>Item {item}</li>
          </Fragment>
        )}
      />
    </ul>
  );
});

Each vs items.map()

If your list items have stable keys, prefer Each.

It is the specialized keyed-list primitive in Qwik and is designed to preserve and move existing rows efficiently.

Use items.map() for simple list rendering or when you do not have a stable key:

<ul>
  {todos.value.map((todo) => (
    <li key={todo.id}>{todo.label}</li>
  ))}
</ul>

Qwik may also be able to optimize some keyed .map() patterns into Each under the hood in the future when it can prove the transformation is safe. For now, use Each explicitly when you want its keyed-row preservation behavior.

Prefer Each when:

  • items have stable ids
  • rows are frequently reordered, inserted, or removed
  • you want to preserve existing row DOM and component instances

Prefer map() when:

  • there is no stable key for the item
  • you want ordinary JSX list rendering without Each's keyed-row preservation behavior
  • the row output should be recomputed from replaced item objects even when the key stays the same

How keyed updates work

Each uses the value from key$ as the identity of the row.

When keys stay the same, Qwik can move and reuse the existing rows instead of recreating them. This is why Each is a good fit for drag-and-drop lists, sortable tables, and other UIs where items move around often.

There is an important tradeoff: if you replace an item object but keep the same key, Each preserves the existing row. That means the row template is not re-run just because a new object with the same key was passed in.

So Each is usually the right choice for keyed collections. It has a different update strategy.

If you need the row output to update from replaced item objects, prefer map(). If you need row-local updates with Each, model that row state with Qwik reactivity instead of relying on replacing the whole item object.

Keys

Choose keys that are stable across renders:

  • Good: database ids, slugs, UUIDs
  • Avoid: array indexes when the list can be reordered

Using unstable keys defeats the main benefit of Each and can produce confusing UI behavior.