Master list rendering in Aurelia with repeat.for. Learn efficient data binding, performance optimization, advanced patterns, and real-world techniques for dynamic collections including arrays, maps, s
The repeat.for binding is Aurelia's powerful list rendering mechanism that creates highly optimized, reactive displays of collection data. It intelligently tracks changes, minimizes DOM updates, and provides rich contextual information for sophisticated data presentation.
Core Concepts
The repeat.for Binding
repeat.for creates a template instance for each item in a collection, similar to a for...of loop but with intelligent DOM management:
<ul><lirepeat.for="item of items"> ${item.name}</li></ul>
JavaScript Analogy:
for (letitemofitems) { // Aurelia creates DOM element for each itemconsole.log(item.name);}
Change Detection and Updates
Aurelia automatically observes collection changes and updates the DOM efficiently:
Important: Use array mutating methods (push, pop, splice, reverse, sort) for automatic detection. Direct index assignment works but requires the array reference to change for detection.
Performance Optimization with Keys
Why Keys Matter
Without keys, Aurelia recreates DOM elements when collections change. With keys, it reuses existing elements:
Key Strategies
Property-based keys (recommended):
Literal property keys (more efficient):
Expression-based keys (flexible but slower):
When to Use Keys
Dynamic collections where items are added, removed, or reordered
Form inputs to preserve user input during updates
Stateful components to maintain component state
Large lists for performance optimization
Sortable/filterable lists
Avoid keys when:
Collection is static or append-only
Items are simple primitives without DOM state
Performance testing shows no benefit
Contextual Properties
Every repeat iteration provides rich contextual information:
Complete Property Reference
Property
Type
Description
$index
number
Zero-based index (0, 1, 2...)
$first
boolean
true for the first item
$last
boolean
true for the last item
$middle
boolean
true for items that aren't first or last
$even
boolean
true for even indices (0, 2, 4...)
$odd
boolean
true for odd indices (1, 3, 5...)
$length
number
Total number of items
$previous
any
null
$parent
object
Parent binding context
Nested Repeats and $parent
Access parent contexts in nested structures:
Accessing Previous Items with $previous
The $previous contextual property provides access to the previous iteration's item, enabling powerful comparison and rendering patterns. It is a computed property available by default as part of repeat's contextual values. You can disable all contextual computed values (including $previous) using the contextual option.
Basic usage:
Key characteristics:
$previous is null for the first item
$previous is undefined when contextual is disabled
Computed property with minimal overhead when enabled (contextual is enabled by default)
Works with all collection types (arrays, Maps, Sets, etc.)
Compatible with keyed repeats
Section Headers and Dividers
A common use case is rendering section headers only when data changes:
Output:
Comparison and Change Indicators
Highlight changes from previous values:
Combining with Keys
$previous works seamlessly with keyed repeats:
Conditional Contextual Properties
Control contextual computed properties (including $previous) based on view model properties:
Performance Considerations
When contextual is disabled:
Zero memory overhead - $previous is not computed
Negligible CPU cost - single conditional check per item
When contextual is enabled (default):
Computed on demand via contextual getter
Minimal CPU cost
Best practices:
Keep contextual enabled unless you have a strong reason to disable it
If needed, disable per-instance with contextual: false or contextual.bind: someBoolean
Data Types and Collections
Arrays
The most common and optimized collection type:
Sets
Useful for unique collections:
Maps
Perfect for key-value pairs:
Number Ranges
Generate sequences quickly:
Advanced Patterns
Destructuring Declarations
Extract multiple values in the repeat declaration:
Integration with Other Template Controllers
Conditional rendering within repeats:
Nested conditionals and repeats:
Working with Async Data
Handle loading states and async operations:
Complex Object Iteration
Use value converters for non-standard collections:
export class MyComponent {
items = [{ name: 'John' }, { name: 'Jane' }];
addItem() {
// Aurelia detects this change and updates DOM
this.items.push({ name: 'Bob' });
}
updateFirst() {
// This change is also detected
this.items[0] = { name: 'Johnny' };
}
}
<!-- Without keys: recreates all DOM on reorder -->
<div repeat.for="user of users">
<input value.bind="user.name">
</div>
<!-- With keys: preserves DOM and form state -->
<div repeat.for="user of users; key.bind: user.id">
<input value.bind="user.name">
</div>
<!-- Use stable, unique properties -->
<li repeat.for="product of products; key.bind: product.id">
${product.name}
</li>
<!-- Show category header only when it changes -->
<div repeat.for="product of products">
<h2 if.bind="product.category !== $previous?.category">
${product.category}
</h2>
<div class="product">${product.name}</div>
</div>
<!-- Enable/disable contextual based on component state -->
<div repeat.for="item of items; contextual.bind: showContextual">
<!-- $previous is only available when contextual is true -->
</div>
export class ProductList {
products = [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Mouse', price: 25 }
];
sortByPrice() {
// Aurelia detects and updates DOM
this.products.sort((a, b) => a.price - b.price);
}
}