Angular 17: *ngFor vs @for Control Flow
Introduction
Angular 17 introduced a fundamentally new approach to template control flow with its built-in blocks (@if, @for, @switch). These replace traditional structural directives like *ngFor, improving performance while offering a more intuitive syntax.
Problem Statement
Developers upgrading to Angular 17 encounter @for syntax in documentation:
@for (car of cars; track car) {
<option [value]="car.value">{{car.viewValue}}</option>
}This replaces familiar *ngFor:
<option *ngFor="let car of cars" [value]="car.value">{{car.viewValue}}</option>Key questions arise:
- Is
@fora direct replacement for*ngFor? - Why was this change introduced?
- What are concrete advantages of
@for? - How should existing projects migrate?
Solution: Understanding Angular's New @for
@for is Angular 17's official replacement for *ngFor, offering significant improvements:
1. Mandatory Performance Optimization
The track parameter is now required, forcing developers to specify a unique identifier. This prevents common performance pitfalls in *ngFor where optional trackBy was often omitted:
<!-- Angular 17 @for with mandatory tracking -->
@for (car of cars; track car.id) {
<app-car [car]="car" />
}The equivalent in *ngFor had no enforced optimization:
<!-- Old *ngFor with optional (and often omitted) trackBy -->
<app-car
*ngFor="let car of cars"
[car]="car" />2. Built-in Empty State Handling
@for provides dedicated syntax for empty collections via @empty:
@for (item of items; track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items found</p> <!-- Rendered when items array is empty -->
}3. Reduced Boilerplate
- No imports: Works without importing
NgFor(critical for standalone components) - Leaner output: Generates ~30% less compiled code than
*ngFor - Structured flow: Explicit curly braces improve template readability
4. Optimized Change Detection
@for computes minimal DOM operations during updates using the track expression, outperforming *ngFor in most cases. The framework handles optimizations internally.
| Feature | *ngFor | @for |
|---|---|---|
| Tracking | Optional trackBy | Mandatory track |
| Empty Collection | Manual ngIf | Built-in @empty |
| Imports | Requires NgFor | No imports |
| Bundle Size | Larger | Smaller |
Migration Guide
1. Automatic Conversion
Use Angular CLI to migrate templates:
ng generate @angular/core:control-flow2. Manual Conversion Steps
Convert this structure:
<element *ngFor="let item of items; index as i; trackBy: trackFn">
{{ item }} {{ i }}
</element>To:
@for (item of items; track trackFn($index, item); let i = $index) {
<element>
{{ item }} {{ i }}
</element>
}Key Notes
trackexpressions must return primitive values (string/number)$index,$count, and$first/$lastprovide context variables- Migration is backward-compatible (old syntax still works)
When Preference Isn't Enough
While some developers initially prefer *ngFor for its one-line conciseness, @for provides concrete advantages that reduce errors and improve performance. The required track parameter alone prevents common UI update issues caused by improper object tracking.
Conclusion
@for represents Angular's future-proofed approach to iterating content:
- ✅ Mandatory tracking improves performance
- ✅ Dedicated
@emptyblock simplifies empty states - ✅ Zero dependencies reduce boilerplate
- ✅ Optimized change detection out-performs
*ngFor
Migrate using the Angular CLI command or manual conversion, and reference the official Control Flow Documentation for edge cases. The new syntax offers tangible benefits despite initial familiarization costs.