Skip to content
Back to All Articles

Angular's New Control Flow Syntax: A Fresh Way to Handle Template Logic

A modern, built-in template experience moving beyond structural directives

Angular's New Control Flow Syntax: A Fresh Way to Handle Template Logic
Angular 17 introduced a modern built-in template syntax, replacing asterisk-heavy structural directives with intuitive native blocks.

TL;DR: When Angular 17 dropped its new control flow syntax, it immediately caught my attention. Having built apps for years with *ngIf, *ngFor, and *ngSwitch, this change feels like a breath of fresh air for readability and maintainability.

  • What’s new: Angular introduces a built-in template syntax (@if, @else, @else if, @for, @switch, @case, @default, @empty) replacing older structural directives.
  • Why it matters: Cleaner templates, easier debugging, and noticeable performance improvements.
  • Tip: The new syntax is future-facing: start using it in new projects and plan for migration using Angular’s official migration guide.

By Javi, Developer and Community Organizer at GDG Central Florida


A Quick Recap of Control Flow in Angular

If you’ve worked with frameworks like .NET Blazor (developed by Microsoft), this might feel familiar. Angular’s new control flow syntax draws inspiration from how Blazor handles conditional and looping logic in Razor templates. It’s a move toward a more intuitive, modern template experience across frameworks.

When I started at Design Interactive, I recall wrestling with structural directives. They were powerful, but wow, they could make templates messy fast. Angular has always been about helping us tame complex UI logic, and with Angular 17’s update, it feels like they’ve taken a huge step in the right direction.

Control flow helps you dynamically decide what gets rendered on the HTML template. Structural directives change the structure of the DOM, and until now, Angular relied on them—like *ngIf and *ngFor—to manage dynamic rendering. Now, this logic is baked directly into the template syntax itself, making it feel more intuitive and much cleaner to manage.


Old vs New Syntax Examples

1. Conditional Rendering

<!-- Old -->
<div *ngIf="isLoggedIn">Welcome back!</div>

<!-- New -->
@if (isLoggedIn) {
  <div>Welcome back!</div>
}

And it doesn’t stop there. Angular brings in powerful new blocks with @else if and @else:

@if (role === 'admin') {
  <admin-dashboard />
} @else if (role === 'editor') {
  <editor-panel />
} @else {
  <user-view />
}

2. Loops (@for) and Empty States (@empty)

<!-- Old -->
<div *ngFor="let item of items">{{ item }}</div>

<!-- New -->
@for (item of items; track item.id) {
  <div>{{ item }}</div>
}

track is required! Angular requires a tracking expression to optimize DOM rendering by tracking item identity across updates.

You can also handle empty states gracefully without wrapping everything in an extra *ngIf:

@for (item of items; track item.id) {
  <div>{{ item.name }}</div>
} @empty {
  <p>No results found.</p>
}

3. Switch Statements

<!-- Old -->
<div [ngSwitch]="status">
  <p *ngSwitchCase="'active'">Active</p>
  <p *ngSwitchCase="'inactive'">Inactive</p>
  <p *ngSwitchDefault>Unknown</p>
</div>

<!-- New -->
@switch (status) {
  @case ('active') {
    <p>Active</p>
  }
  @case ('inactive') {
    <p>Inactive</p>
  }
  @default {
    <p>Unknown</p>
  }
}

Quick Reference Table

FeatureOld SyntaxNew SyntaxWhen to Use
Conditional*ngIf@ifShow or hide elements based on a boolean condition.
Else IfChained *ngIf@else ifUse multiple conditional branches cleanly.
Else<ng-template> fallback@elseRender fallback content when a condition is false.
Looping*ngFor@for + trackRender a list of items. track is required for efficient updates.
Empty State*ngIf="list.length === 0"@emptyShow a placeholder message when a loop collection is empty.
Switch Cases[ngSwitch], *ngSwitchCase@switch, @case, @defaultRender different elements based on a matching variable.

⚠️ Important: As of Angular 20, the old structural directives (*ngIf, *ngFor, etc.) are officially deprecated. The new control flow syntax (@if, @for, @switch) is now the standard.


⚡ Why This Matters: Key Benefits

  • Cleaner Templates: Say goodbye to asterisk-heavy syntax. This new style feels like writing plain JavaScript logic directly in your HTML.
  • Performance Gains: The new control flow is faster because it uses internal Angular APIs to create and destroy views directly, instead of relying on structural directive abstractions. This means less runtime overhead and better rendering performance.
  • Better DX (Developer Experience): Templates are easier to read, write, and debug, especially in complex or enterprise-scale applications.

✅ Best Practices & Migration

  • Use the new syntax in greenfield projects: It aligns with the current and future direction of Angular.
  • Use @else if and @empty: Simplify conditional logic and eliminate nested templates.
  • Always include a track expression: It’s mandatory with @for to help Angular optimize DOM rendering.
  • Keep template logic clean: Handle complex logic and heavy filtering in your component class or reactive stores, not in the template.
  • Automated Migration CLI Schematic: To migrate existing codebases, run the official Angular CLI schematic:
    ng g @angular/core:control-flow
    It refactors your templates automatically!

📚 Resources


🤝 Let’s Connect

I love sharing, teaching, and learning with other developers. Let’s build cool software together:

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
— Martin Fowler

These tools empower us to write cleaner, smarter code with less stress. Let’s keep learning and building, one component at a time!

¡Hasta la próxima! 👋

Javi
Software Engineer & GDG Community Organizer
techno-logic.us

Let's Connect

Feel free to reach out for opportunities, collaborations, or just to say hello.

Email copied to clipboard!