--- title: Use {{#each}} with @key for Lists impact: MEDIUM impactDescription: 50-100% faster list updates tags: templates, each, performance, rendering --- ## Use {{#each}} with @key for Lists Use the `key=` parameter with `{{#each}}` when objects are recreated between renders (e.g., via `.map()` or fresh API data). The default behavior uses object identity (`@identity`), which works when object references are stable. **Incorrect (no key):** ```glimmer-js // app/components/user-list.gjs import UserCard from './user-card'; ``` **Correct (with key):** ```glimmer-js // app/components/user-list.gjs import UserCard from './user-card'; ``` **For arrays of primitives (strings, numbers):** `@identity` is the default, so you rarely need to specify it explicitly. It compares items by value for primitives. ```glimmer-js // app/components/tag-list.gjs ``` **For complex scenarios with @index:** ```glimmer-js // app/components/item-list.gjs ``` Using proper keys allows Ember's rendering engine to efficiently update, reorder, and remove items without re-rendering the entire list. **When to use `key=`:** - Objects recreated between renders (`.map()`, generators, fresh API responses) → use `key="id"` or similar - High-frequency updates (animations, real-time data) → always specify a key - Stable object references (Apollo cache, Ember Data) → default `@identity` is fine - Items never reorder → `key="@index"` is acceptable **Performance comparison (dbmon benchmark, 40 rows at 60fps):** - Without key (objects recreated): Destroys/recreates DOM every frame - With `key="data.db.id"`: DOM reuse, **2x FPS improvement** ### References: - [Ember API: each helper](https://api.emberjs.com/ember/release/classes/Ember.Templates.helpers/methods/each) - [Ember template lint: equire-each-key](https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/require-each-key.md) - [Example PR showing the fps improvement on updated lists](https://github.com/universal-ember/table/pull/68)