Render Lists in LWC – for:each & iterator Directives
Learn how to use for:each and iterator directives in Lightning Web Components to efficiently render dynamic lists — and understand the key differences, rules, and best practices for each approach.
1 Introduction
Rendering lists is one of the most common tasks in any UI framework. In Lightning Web Components (LWC), you have two built-in directives for iterating over arrays in your HTML template:
• for:each — The standard directive for looping over an array and rendering a block of HTML for each item.
• iterator — An advanced directive that provides extra metadata (first, last, index, value) for each item — useful when you need to style the first or last item differently.
key, LWC will throw an error. The key must be unique and stable for each item.
Both directives are applied on a <template> tag and do not add any extra DOM element to the rendered page.
2 for:each — Syntax & Basic Example
for:each iterates over an array. You must also declare a loop variable using for:item, and optionally the index using for:index.
<template for:each={arrayProperty} for:item="itemVar" for:index="indexVar">
<template>
<lightning-card title="Fruit List" icon-name="utility:list">
<ul class="slds-p-around_medium">
<template for:each={fruits} for:item="fruit">
<li key={fruit.id}>
{fruit.name}
</li>
</template>
</ul>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class FruitList extends LightningElement {
fruits = [
{ id: 1, name: '🍎 Apple' },
{ id: 2, name: '🍌 Banana' },
{ id: 3, name: '🍇 Grapes' },
{ id: 4, name: '🍊 Orange' },
{ id: 5, name: '🍓 Strawberry' }
];
}
• for:each={fruits} binds the array from the JS class.
• for:item="fruit" declares the loop variable — use it as {fruit.id}, {fruit.name} etc.
• key={fruit.id} on the <li> (the first child) tells LWC how to track elements efficiently.
3 for:each with for:index — Using the Index
You can also access the current index of each item using the for:index attribute. This is useful for displaying row numbers or conditional logic based on position.
<template>
<lightning-card title="Team Members" icon-name="utility:people">
<div class="slds-p-around_medium">
<template for:each={teamMembers} for:item="member" for:index="idx">
<div key={member.id} class="slds-m-bottom_small">
<b>{idx}.</b> {member.name} — {member.role}
</div>
</template>
</div>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class IndexedList extends LightningElement {
teamMembers = [
{ id: 'u1', name: 'Alice Johnson', role: 'Developer' },
{ id: 'u2', name: 'Bob Smith', role: 'Designer' },
{ id: 'u3', name: 'Carol White', role: 'Product Manager' },
{ id: 'u4', name: 'David Lee', role: 'QA Engineer' }
];
}
• for:index="idx" gives the zero-based position of each item in the array.
• The index is a read-only value inside the template — you cannot modify the array through it.
• Use string IDs (like 'u1') for the key — they are more stable than numeric array indexes.
key (e.g., key={idx}). If the array is reordered or filtered, LWC will incorrectly reuse DOM nodes. Always use a unique, stable identifier from the data itself.
4 iterator — Syntax & Basic Example
The iterator directive provides richer metadata for each loop iteration. Instead of a plain item variable, each iteration exposes an object with four properties:
• value — The current item from the array.
• index — The zero-based position.
• first — true if this is the first item.
• last — true if this is the last item.
<template iterator:iteratorVar={arrayProperty}>
<template>
<lightning-card title="Product List" icon-name="utility:product_consumed">
<ul class="slds-p-around_medium">
<template iterator:prod={products}>
<li key={prod.value.id}
class={prod.value.itemClass}>
<template lwc:if={prod.first}>
<span class="slds-badge">⭐ Top Pick</span>
</template>
{prod.value.name} — ${prod.value.price}
<template lwc:if={prod.last}>
<span class="slds-badge slds-badge_inverse">End of List</span>
</template>
</li>
</template>
</ul>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class IteratorDemo extends LightningElement {
products = [
{ id: 'p1', name: 'Laptop Pro', price: 1299 },
{ id: 'p2', name: 'Wireless Mouse', price: 49 },
{ id: 'p3', name: 'USB-C Hub', price: 79 },
{ id: 'p4', name: 'Monitor 4K', price: 599 }
];
}
• iterator:prod={products} — prod is the iterator variable for each iteration.
• Access item data via {prod.value.name} — the actual object is under .value.
• {prod.first} is true only on the first item — perfect for adding a "Top Pick" badge.
• {prod.last} is true only on the last item — useful for "End of List" markers or removing borders.
5 Practical Example — Dynamic Table with iterator
A common real-world use case: render a table of records where the first row gets a highlighted style and the last row has no bottom border. This is trivial with iterator.
<template>
<lightning-card title="Employee Directory" icon-name="utility:groups">
<div class="slds-p-around_medium">
<table class="slds-table slds-table_bordered slds-table_cell-buffer">
<thead>
<tr class="slds-line-height_reset">
<th scope="col">Name</th>
<th scope="col">Department</th>
<th scope="col">Salary</th>
</tr>
</thead>
<tbody>
<template iterator:emp={employees}>
<tr key={emp.value.id}
class={emp.value.rowClass}>
<td>
<template lwc:if={emp.first}>
⭐
</template>
{emp.value.name}
</td>
<td>{emp.value.department}</td>
<td>${emp.value.salary}</td>
</tr>
</template>
</tbody>
</table>
</div>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class EmployeeTable extends LightningElement {
employees = [
{ id: 'e1', name: 'Sarah Connor', department: 'Engineering', salary: 120000 },
{ id: 'e2', name: 'John Doe', department: 'Marketing', salary: 85000 },
{ id: 'e3', name: 'Jane Smith', department: 'Sales', salary: 95000 },
{ id: 'e4', name: 'Mike Johnson', department: 'Finance', salary: 105000 }
];
}
• The iterator automatically identifies emp.first → the star icon is conditionally shown only on the first employee row.
• key is placed on the <tr> element — the first child of the iterator template.
• lwc:if and iterator work together seamlessly — no extra JavaScript needed for first/last detection.
6 Nested for:each — Lists Within Lists
You can nest for:each directives to render nested data structures, like a list of categories each containing sub-items.
<template>
<lightning-card title="Menu Categories" icon-name="utility:rows">
<div class="slds-p-around_medium">
<template for:each={menuCategories} for:item="category">
<div key={category.id} class="slds-m-bottom_medium">
<h3 style="font-weight:bold; color:#032D60;">
{category.name}
</h3>
<ul class="slds-m-top_x-small">
<template for:each={category.items} for:item="item">
<li key={item.id} class="slds-m-left_medium">
{item.label}
</li>
</template>
</ul>
</div>
</template>
</div>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class NestedList extends LightningElement {
menuCategories = [
{
id: 'c1',
name: '🍔 Mains',
items: [
{ id: 'i1', label: 'Burger' },
{ id: 'i2', label: 'Pasta' },
{ id: 'i3', label: 'Pizza' }
]
},
{
id: 'c2',
name: '🍰 Desserts',
items: [
{ id: 'i4', label: 'Ice Cream' },
{ id: 'i5', label: 'Cake' }
]
},
{
id: 'c3',
name: '🥤 Drinks',
items: [
{ id: 'i6', label: 'Juice' },
{ id: 'i7', label: 'Lemonade' }
]
}
];
}
• Each level of nesting requires its own unique key on the first child element of that loop.
• The outer loop uses key={category.id} on the <div>.
• The inner loop uses key={item.id} on each <li>.
• Ensure all IDs in the entire nested structure are globally unique to avoid key collisions.
7 for:each vs iterator — Comparison
| Aspect | for:each | iterator |
|---|---|---|
| Syntax | for:each={arr} for:item="x" |
iterator:x={arr} |
| Access item value | {x.property} |
{x.value.property} |
| Access index | for:index="idx" → {idx} |
{x.index} (built-in) |
| First item detection | ❌ Not available | ✅ {x.first} |
| Last item detection | ❌ Not available | ✅ {x.last} |
| Use case | Standard list rendering | When first/last styling needed |
| Complexity | Simpler syntax | Slightly more verbose |
| key requirement | ✅ Required on first child | ✅ Required on first child |
8 Key Rules & Best Practices
| Rule | Detail |
|---|---|
| key is mandatory | Every loop must have a key attribute on the first child of the template. LWC uses it for efficient DOM reconciliation. |
| key must be unique & stable | Use a record ID or a unique field from your data. Never use the array index as the key — it causes bugs when items are reordered or deleted. |
| key must be a string | The key value must be a primitive (string or number). Avoid using objects as keys. |
| Only on <template> tags | Both for:each and iterator must be placed on <template> tags, not on regular HTML elements. |
| Use getter for computed arrays | If you need to filter, sort, or transform the array before rendering, use a JavaScript get function. Never mutate the original tracked array directly. |
| iterator for first/last only | Only use iterator when you actually need .first or .last. For everything else, prefer the simpler for:each syntax. |
| No expressions in for:each | The array property must be referenced as {myArray} — you cannot write inline expressions like {myArray.filter(...)} in the template. Use a getter instead. |
9 Quick Reference
for:each (basic):
<template for:each={myArray} for:item="item">
<div key={item.id}>{item.name}</div>
</template>
for:each with index:
<template for:each={myArray} for:item="item" for:index="idx">
<div key={item.id}>{idx}. {item.name}</div>
</template>
iterator (with first / last):
<template iterator:it={myArray}>
<div key={it.value.id}>
<template lwc:if={it.first}>
<span>First Item!</span>
</template>
{it.value.name} <!-- Index: {it.index} -->
<template lwc:if={it.last}>
<span>Last Item!</span>
</template>
</div>
</template>
Getter for filtered/sorted list:
// In your JS class — compute the list in a getter
get sortedEmployees() {
return [...this.employees].sort((a, b) => a.name.localeCompare(b.name));
}
<!-- In your template -->
<template for:each={sortedEmployees} for:item="emp">
<div key={emp.id}>{emp.name}</div>
</template>
for:each for standard lists. Use iterator only when you need .first / .last metadata. Always provide a unique, stable key on the first child — never use the array index as the key.