Angular Control Flow (@if / @for / @switch): Syntax, track, and Why It's Faster
Quick answer
Angular 17+ replaces the *ngIf and *ngFor structural directives with built-in @if/@else, @for, and @switch control-flow blocks in templates. It is compiled directly instead of being implemented as directives, so it needs no CommonModule import and produces smaller, faster output. @for requires a track expression (e.g. track item.id) — that is mandatory and is what makes list re-rendering fast; it also adds an @empty block.
Short answer: Angular 17+ replaces *ngIf/*ngFor/*ngSwitch with built-in @if / @for / @switch control-flow blocks. They're compiled directly (no CommonModule import, smaller/faster output), and @for requires a track expression — that's mandatory and is what makes list updates fast.
Angular's control flow moved from structural directives (*ngIf) to built-in template blocks (@if). It's more readable, needs no module import, and compiles to faster code — but the syntax has a few gotchas interviewers love (especially track).
@if / @else
@if (user()) {
<p>Welcome, {{ user().name }}</p>
} @else if (loading()) {
<spinner />
} @else {
<p>Please sign in</p>
}No asterisks, no <ng-template> for the else case — the branches are real template blocks.
@for — and the mandatory track
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
} @empty {
<li>No items yet</li>
}Two things trip people up:
- There is no
let. It's@for (item of items; track item.id), not@for (let item of items). trackis required. In*ngFor,trackBywas optional; in@forit's mandatory. Track a stable identity (item.id), not$index, so Angular can reuse DOM nodes across updates instead of re-rendering the list.
Index and other context come from contextual variables:
@for (item of items; track item.id; let i = $index, let isLast = $last) {
<li>{{ i }}: {{ item.name }}{{ isLast ? '' : ',' }}</li>
}$index, $first, $last, $even, $odd, $count are all available.
@switch
@switch (role()) {
@case ('admin') { <admin-panel /> }
@case ('editor') { <editor-panel /> }
@default { <guest-panel /> }
}Why it's faster (the interview point)
The old directives were implemented as directives — runtime machinery that had to be imported (CommonModule) and interpreted. Control flow is compiled directly into the component's output, so there's less runtime code, smaller bundles, and the mandatory track guarantees efficient list diffing. That "compiled, not a directive" line is the answer interviewers want.
Common interview traps
- Writing
@for (let item of items)— theletis invalid; and forgettingtrackis a compile error. track $index— works but defeats node reuse when items reorder; track a stable id.- Still importing
CommonModulefor these — unnecessary now.
Related guides
Sources
Key takeaways
- •@if / @for / @switch are built-in template syntax, not directives — no CommonModule import needed.
- •@for REQUIRES a track expression (track item.id); it's mandatory and drives efficient list diffing.
- •Write @for (item of items; track item.id) — there is no `let`; get the index via `let i = $index`.
- •@for adds an @empty block for the empty-list case; @if supports @else if / @else.
- •It compiles directly, so it's faster and produces less code than the structural-directive equivalents.
Frequently asked questions
What replaced *ngIf and *ngFor in Angular?
Built-in control flow: @if/@else replace *ngIf, @for replaces *ngFor, and @switch replaces *ngSwitch. They are template syntax compiled by Angular rather than structural directives, so you no longer import CommonModule for them, and the output is smaller and faster.
Is track required in Angular's @for?
Yes. Unlike *ngFor where trackBy was optional, @for requires a track expression, e.g. @for (item of items; track item.id). Tracking by a stable identity lets Angular reuse DOM nodes across updates instead of re-rendering the whole list, which is the main performance win.
How do I get the index in @for?
Use the contextual variable $index, assigned with let: @for (item of items; track item.id; let i = $index). Other contextual variables include $first, $last, $even, $odd, and $count.
Software Engineering Leader & Technical Author · Updated August 26, 2026