Files
marco/.agents/skills/ember-best-practices/rules/template-each-key.md

2.6 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use {{#each}} with @key for Lists MEDIUM 50-100% faster list updates 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):

// app/components/user-list.gjs
import UserCard from './user-card';

<template>
  <ul>
    {{#each this.users as |user|}}
      <li>
        <UserCard @user={{user}} />
      </li>
    {{/each}}
  </ul>
</template>

Correct (with key):

// app/components/user-list.gjs
import UserCard from './user-card';

<template>
  <ul>
    {{#each this.users key="id" as |user|}}
      <li>
        <UserCard @user={{user}} />
      </li>
    {{/each}}
  </ul>
</template>

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.

// app/components/tag-list.gjs
<template>
  {{! @identity is implicit, no need to write it }}
  {{#each this.tags as |tag|}}
    <span class="tag">{{tag}}</span>
  {{/each}}
</template>

For complex scenarios with @index:

// app/components/item-list.gjs
<template>
  {{#each this.items key="@index" as |item index|}}
    <div data-index={{index}}>
      {{item.name}}
    </div>
  {{/each}}
</template>

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: