Lightning Web Components: Iterators (for:each
& iterator) — Deep Dive + Real Interview Q&A
1) Why Iterators Matter in LWC
Rendering lists is a core UI requirement in Salesforce apps — from Opportunities on an
Account to Line Items in a Quote. LWC provides two main ways to loop over collections in
templates: the simple and fast 'for:each' and the more powerful 'iterator' directive which
exposes first/last and surrounding items for advanced UIs.
for:each → lightweight, most common; iterate arrays quickly.
iterator → exposes special properties (first, last, index, previous, next) for context-
aware rendering.
Both require a stable 'key' for efficient DOM diffing and re-rendering.
2) for:each vs iterator — Key Differences
API: for:each={array} for:item='item' for:index='index' vs iterator:it={array}
Context: 'for:each' gives you 'item' and optional 'index'; 'iterator' gives '[Link]', '[Link]',
'[Link]', '[Link]', '[Link]', '[Link]'.
Use-cases: 'for:each' for simple lists; 'iterator' when you need separators, grouping
headers, drag handles, first/last badges, or to compare neighboring records.
Performance: Both are virtual-DOM friendly; correctness of 'key' impacts re-render cost
more than choice of directive.
3) Syntax Cheatsheet
3.1) for:each basic
<template for:each={contacts} for:item="con" for:index="i">
<p key={[Link]}>{i + 1}. {[Link]} — {[Link]}</p>
</template>
3.2) iterator for advanced rendering
<template iterator:it={contacts}>
<div key={[Link]}>
<template if:true={[Link]}>
<h3>Team Contacts</h3>
</template>
<p>{[Link] + 1}. {[Link]}</p>
<template if:false={[Link]}>
<hr/>
</template>
</div>
</template>
4) Real-Life Scenario: Quote Line Items with Subtotals
Business ask: In a Quote screen, show line items grouped by Product Family with a
subtotal after each family, and a grand total at the end. We also need a subtle divider
between items, except after the last item in a group.
Controller (Apex — wireable)
public with sharing class QuoteItemsController
{ @AuraEnabled(cacheable=true)
public static List<QuoteLineItem> fetchItems(Id quoteId) {
return [SELECT Id, Quantity, UnitPrice, TotalPrice, [Link],
[Link]
FROM QuoteLineItem WHERE QuoteId = :quoteId ORDER BY
[Link], [Link]];
}
}
LWC JS
import { LightningElement, wire, api } from 'lwc';
import fetchItems from '@salesforce/apex/[Link]';
export default class QuoteItems extends LightningElement
{ @api recordId;
items = [];
grouped = [];
total = 0;
@wire(fetchItems, { quoteId: '$recordId' })
wired({ data, error }) {
if (data) {
[Link] = [Link]((x) =>
({ id: [Link],
name: [Link],
family: [Link],
qty: [Link],
price: [Link],
total: [Link]
}));
// group by family
const m = new Map();
for (const it of [Link]) {
if () [Link]([Link], []);
[Link]([Link]).push(it);
}
[Link] = [Link]([Link]()).map(([family, rows]) =>
({ family, rows, subtotal: [Link]((s, r) => s + [Link], 0)
}));
[Link] = [Link]((s, g) => s + [Link], 0);
} else if (error) {
// handle error
// eslint-disable-next-line no-console
[Link](error);
}
}
}
LWC HTML using iterator
<template>
<template for:each={grouped} for:item="grp">
<section key={[Link]} class="family">
<h3>{[Link]}</h3>
<template iterator:it={[Link]}>
<div key={[Link]} class="row">
<span>{[Link]}</span>
<span>{[Link]} × {[Link]}</span>
<span>= {[Link]}</span>
</div>
<template if:false={[Link]}>
<div class="divider"></div>
</template>
</template>
<p class="subtotal">Subtotal: {[Link]}</p>
</section>
</template>
<h2>Grand Total: {total}</h2>
</template>
5) Best Practices
Always provide a stable key (record Id or deterministic composite key).
Avoid using array index as key when the array can reorder; it causes incorrect re-use of
DOM nodes.
Pre-compute data in JS (grouping, totals) to keep templates clean and fast.
Use iterator when you need first/last or to compare neighbors; otherwise default to
for:each for readability.
Guard against null/undefined arrays before rendering to prevent template errors.
When mutating arrays, create new arrays (spread [...arr]) to trigger reactivity.
6) Real Interview Q&A
Q1. What they asked me: What's the difference between 'for:each' and 'iterator' in
LWC?
Ans: for:each is a lightweight loop that exposes each item and optional index. iterator
wraps each item as an object exposing 'value', 'first', 'last', 'index', and neighbors. I use
iterator when the UI depends on boundaries (first/last) or needs separators/group
headers.
Tips:
Start with a one-liner difference, then give 1–2 concrete use-cases like adding <hr> between
items or group subtotals.
Q2. What they asked me: When would you prefer 'iterator' over 'for:each'?
Ans: Whenever I need context of the current item's position — e.g., show a header before
the first item, hide a divider after the last, compare with previous to decide if a new
date/group starts.
Tips:
Mention 'first' and 'last' explicitly; interviewers expect those keywords.
Q3. What they asked me: Why is 'key' mandatory and how do you choose it?
Ans: The key allows LWC’s diffing algorithm to map data items to DOM nodes across
renders, preventing flicker and preserving component state. I pick a stable unique Id (e.g.,
record Id). I avoid array indexes if the order can change.
Tips:
Add a quick pitfall: using index as key breaks when array mutates.
Q4. What they asked me: How do you render a separator between items except the
last one?
Ans: Use iterator and wrap the separator in <template if:false={[Link]}>. That way the last
item won’t render a trailing line.
Tips:
If asked to code, write 3–4 lines of template showing [Link].
Q5. What they asked me: How can you show a 'No records' message safely?
Ans: Guard with conditional template: <template
if:true={[Link]}>...list...</template><template if:false={[Link]}>No
records</template> and default records to [].
Tips:
Mention initializing arrays to [] to avoid undefined errors.
Q6. Explain how you'd implement alternating row backgrounds (zebra rows).
Ans: Use the index (for:each with for:index or iterator's [Link]) and compute a class:
class={[Link] % 2 ? 'odd' : 'even'}.
Tips:
Call out that index is fine for CSS decisions but not as a DOM key.
Q7. What they asked me: How do you group records and show subtotals per group?
Ans: Compute groups in JS (Map by key), render outer for:each over groups, then inner
iterator over rows to place separators and subtotals.
Tips:
Stress separation of concerns: complex logic → JS, simple loop → HTML.
Q8. What they asked me: What happens if you forget 'key' on a looped element?
Ans: LWC will throw a compilation error because a keyed element is required for list
rendering to ensure deterministic diffing.
Tips:
Add that the key must be on the topmost repeated element inside the loop.
Q9. Can you access previous item’s value inside 'for:each'?
Ans: Not directly. 'for:each' only gives item/index. To access neighbors in template, switch
to 'iterator' which gives '[Link]' and '[Link]' or compute needed flags in JS.
Tips:
Offer both routes: iterator or precompute flags in JS.
Q10. What they asked me: How do you re-render after pushing to an array?
Ans: Assign a new array reference: [Link] = [...[Link], newItem]; to trigger reactivity.
Tips:
Also mention immutability for predictable updates.
Q11. What they asked me: Show code to render contacts and add a header only once
before the first row.
Ans: Use iterator: <template iterator:it={contacts}><template
if:true={[Link]}><h3>Contacts</h3></template><p
key={[Link]}>{[Link]}</p></template>.
Tips:
Keep it minimal; interviewers evaluate clarity.
Q12. What they asked me: How to handle very large lists efficiently?
Ans: Paginate or virtualize; don’t render thousands at once. Use server-side paging or
lightning-datatable with pagination, and only render what’s visible.
Tips:
Tie back to governor limits indirectly: server round-trips with cacheable Apex.
7) Common Pitfalls with Examples
<!-- BAD: Using index as key (order can change) -->
<template for:each={rows} for:item="r" for:index="i">
<div key={i}>{[Link]}</div>
</template>
<!-- GOOD: Use stable Id -->
<template for:each={rows} for:item="r">
<div key={[Link]}>{[Link]}</div>
</template>
8) Quick Checklist Before You Ship
Do I really need iterator? (Only if first/last/neighbor logic is required)
Are my keys stable and unique?
Is business logic in JS and UI kept simple?
Do I handle empty/errored states gracefully?
Have I tested reactivity after array mutations?
9) Copy-Paste Snippets
<!-- Separator except last -->
<template iterator:it={rows}>
<div key={[Link]}>{[Link]}</div>
<template if:false={[Link]}><hr/></template>
</template>
<!-- Header on first only -->
<template iterator:it={rows}>
<template if:true={[Link]}><h3>Header</h3></template>
<div key={[Link]}>{[Link]}</div>
</template>