Angular Control Flow Converter
Convert *ngIf, *ngFor and ngSwitch to @if, @for and @switch. Handles else templates, trackBy, index aliases and inline component templates, using the official migration rules.
<section class="orders">
@if (user$ | async; as user) {
<h2>Orders for {{ user.name }}</h2>
} @else {
<app-spinner />
}
@if (orders.length) {
<ul>
@for (order of orders; track trackById(i, order); let i = $index, last = $last) {
<li [class.last]="last">
{{ i + 1 }}. {{ order.title }}
@switch (order.status) {
@case ('shipped') {
<span class="badge">Shipped</span>
}
@case ('pending') {
<span class="badge">Pending</span>
}
@default {
<span class="badge">Unknown</span>
}
}
</li>
}
</ul>
} @else {
<p>No orders yet.</p>
}
</section>Converted 2 @if, 1 @for, 1 @switch.
You can remove NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault from the component's imports. Keep CommonModule only if the template still uses its pipes, such as async or date.
From Structural Directives to Built-in Control Flow
Angular 17 added @if, @for and @switch blocks to the template syntax. They need no imports, and @for requires a track expression, which prevents the common mistake of re-rendering a whole list because trackBy was forgotten. Paste a template above and the converter rewrites every *ngIf, *ngFor and ngSwitch, and leaves everything else exactly as you wrote it.
Syntax Mapping
| Structural directive | Control flow |
|---|---|
| *ngIf="cond" | @if (cond) { … } |
| *ngIf="cond; else other" | @if (cond) { … } @else { … } |
| *ngIf="cond; then a; else b" | @if (cond) { a } @else { b } |
| *ngIf="user$ | async as user" | @if (user$ | async; as user) { … } |
| *ngFor="let item of items" | @for (item of items; track item) { … } |
| trackBy: trackById | track trackById($index, item) |
| let i = index / index as i | let i = $index |
| let isLast = last | let isLast = $last |
| [ngSwitch]="x" + *ngSwitchCase="a" | @switch (x) { @case (a) { … } } |
| *ngSwitchDefault | @default { … } |
| <ng-template [ngIf]> / ngFor [ngForOf] | Same blocks; the ng-template wrapper is removed |
How to Use the Converter
- Paste a template, either an .html file or a whole component with an inline
template. - Choose how to track loops that have no trackBy. Tracking by a unique id is the fastest option.
- Copy the result and read the notes under it: which imports to remove, and anything left unchanged.
- Run your tests, especially for lists whose items change in place.
Frequently Asked Questions
- Are *ngIf, *ngFor and ngSwitch deprecated?
- Yes. NgIf, NgFor and NgSwitch have been deprecated since Angular 20 in favor of @if, @for and @switch. They still ship in Angular 22, marked for removal in a future major release, so migrating now avoids a forced change later.
- Should I use this tool or ng generate @angular/core:control-flow?
- For a whole project, run the official schematic, which ng update also offers. This converter is for a single template or component: reviewing a change, converting a file the schematic skipped, or learning the new syntax. It follows the same rules, so the output matches the schematic.
- Why does the output use "track item"?
- That is what the official migration writes for loops without a trackBy. Tracking by object identity works but is the slowest option, so if your items have a unique id, choose "track item.<property>" and enter it. Loops with a trackBy function become track fn($index, item).
- What happens to my else and then templates?
- If an <ng-template #ref> is only used by that one *ngIf, its content moves into the @if or @else block and the template is removed. If something else also uses it, the template stays and the block renders it with ngTemplateOutlet, so add NgTemplateOutlet to the component imports.
- Can the converted template behave differently?
- One documented difference: when a tracked property changes but the object stays the same, @for updates the existing view instead of destroying and recreating it, as *ngFor would. Check components that rely on being recreated, and run your tests after converting.
- Which Angular version do I need?
- Built-in control flow arrived in Angular 17 and became stable in Angular 18. After converting, remove NgIf, NgFor and NgSwitch from the component imports.
- Is my code uploaded anywhere?
- No. The conversion runs entirely in your browser.
Sources & Official Resources
The conversion rules follow Angular's control flow guide and the official migration schematic. Last reviewed against Angular 22.
- Control flow guide (angular.dev)
- Migration to control flow syntax (angular.dev)
- NgIf API and deprecation notice (angular.dev)
- Official control-flow migration schematic source (GitHub)
Upgrading as well? The Angular Upgrade Planner lists every step between two versions. More in Angular tools.