Published on

Why I Stopped Using ngIf and ngFor in Angular

I’ve been using Angular for quite some time, and like many developers, I got used to writing *ngIf, *ngFor, and ngSwitch in almost every component. They worked fine until Angular introduced a new syntax that completely changed how I write templates.

I’m talking about the new control flow syntax:
@if, @else, @for, @let, @switch, @case, and @default.

At first, I didn’t pay much attention. But after giving them a try, I quickly realized how much cleaner, simpler, and more natural my templates became.
Here’s why I stopped using the old syntax and why you might want to as well.

The Problem with the Old Syntax

The old *ngIf, *ngFor, and ngSwitch directives worked well, but they had a few downsides:

  • The asterisks * looked strange to new developers.

  • You often needed extra <ng-container> or <ng-template> tags.

  • Nested conditions and loops were harder to read.

Angular’s new syntax fixes all of that. It’s built directly into the template engine, meaning no more directives — just clean, readable control flow.

@if and @else

The new @if syntax replaces *ngIf and feels much closer to regular JavaScript logic.

Before:

<div *ngIf="isLoggedIn; else guestPart">
  Welcome back!
</div>

<ng-template #guestPart>
  Please log in.
</ng-template>

Now:

@if (isLoggedIn) {
  <div>Welcome back!</div>
} @else {
  <div>Please log in.</div>
}

✅ Easier to read
✅ No <ng-template> blocks or template references
✅ Perfect for nesting conditions

@for

The classic *ngFor still works, but @for feels cleaner and reads more like plain JavaScript.

Before:

<ul>
  <li *ngFor="let user of users; trackBy: trackById">
    {{ user.name }}
  </li>
</ul>

Now:

<ul>
  @for (user of users; track user.id) {
    <li>{{ user.name }}</li>
  }
</ul>

✅ Simpler syntax (track instead of trackBy)
✅ No asterisk confusion
✅ Easier to reason about

@let

Sometimes you need to store a local variable in your template.
@let makes that super easy.

Before:

<ng-container *ngIf="user as u">
  <p>Hello {{ u.name }}</p>
</ng-container>

Now:

@let u = user;
<p>Hello {{ u.name }}</p>

✅ Cleaner variable handling
✅ Works outside @if blocks
✅ Makes templates easier to follow

@switch, @case, and @default

The new @switch syntax replaces ngSwitch and feels just like a real switch statement in JavaScript.

Before:

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

Now:

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

✅ More readable
✅ No extra attributes
✅ Matches JavaScript logic perfectly

How to Start Using It

You’ll need Angular 17 or newer to use this new syntax.
If you’re upgrading, just run:

ng update @angular/core@latest @angular/cli@latest

No extra setup or imports needed — it’s ready out of the box.

Summary

Old Syntax

New Syntax

Purpose

*ngIf / else

@if / @else

Conditional rendering

*ngFor

@for

Loops

*ngSwitch

@switch, @case, @default

Switch logic

as syntax

@let

Local variable binding

Final Thoughts

After switching to the new syntax, my templates feel much cleaner and easier to maintain.
I don’t need to wrap everything in containers or remember special * rules anymore.

If you’ve been using Angular for a while, try replacing a few *ngIf or *ngFor blocks with the new syntax and you’ll instantly see the difference.

Sometimes small changes make a big impact and this one definitely does.