Components

Angular @if: Complete Guide

A complete guide to all the features of the @if template syntax, including the most frequently asked questions bout this syntax, as well as an anti-pattern to avoid.

Angular @if: Complete Guide — Components article by Angular University on Angular In Depth
Angular @if: Complete Guide — Components article by Angular University on Angular In Depth
On this page · 14 sections

One of the most impactful updates to the Angular framework was the arrival of the new built-in control flow syntax, first introduced in Angular 17.

This syntax addresses one of the core tasks we handle constantly as developers: toggling the visibility of page elements based on a logical condition.

Previously, this was accomplished using the classic structural directive *ngIf.

However, we now have a superior option to ngIf: the new @if syntax, a key part of the modern template control flow.

We will examine both approaches, explore why the new @if syntax is superior, and show how to switch to it automatically.

This guide will explain why the new @if is a significant improvement, offering a more straightforward and user-friendly alternative to *ngIf.

We will also address a frequent anti-pattern developers encounter related to using @if together with the async pipe.

Excited to enhance your Angular expertise? Let's begin!

Table of Contents

This article covers the following subjects:

  • Why introduce a new control flow syntax?
  • Understanding Angular @if
  • The @if syntax
  • Implementing @if with else
  • Using @if with else if and else
  • The reason @if requires no import
  • The reason the * syntax is obsolete
  • What makes the new @if syntax superior to ngIf?
  • How to migrate to the new @if syntax?
  • Comparing @if with CSS-based element hiding
  • The anti-pattern of multiple nested @if with the async pipe
  • Summary

Note: If you're interested in the older *ngIf syntax, please refer to this guide: Angular *ngIf: Complete Guide

For details on the new @switch feature for conditional rendering, see this guide: Angular @switch: Complete Guide.

Why introduce a new control flow syntax?

As explained by Minko Gechev in the announcement introducing @if, this new built-in control flow syntax provides:

  • A more ergonomic and concise syntax that resembles JavaScript more closely, making it more intuitive and reducing the need for frequent documentation checks.

  • Template features like @if are ready to use in your templates without requiring additional imports

  • Improved type checking facilitated by more effective type narrowing

  • A structure that is largely processed at build-time, reducing the runtime overhead and helping you decrease your bundle size and enhance Core Web Vitals.

  • The new control flow will also simplify the implementation of signal-based change detection in the future.

For comprehensive information on @if and the new template syntax, refer to the official documentation: Angular Control Flow.

Understanding Angular @if

In Angular, @if plays the same role that if plays in JavaScript.

@if is a new template syntax for rendering elements conditionally within Angular templates. It is a built-in control flow construct that enables you to display or conceal page elements in accordance with a boolean condition.

In contrast to the older *ngIf structural directive, there's no need to import @if for use in standalone components; it's universally available.

It provides a more succinct and readable syntax, and it also accommodates else if and else clauses, something the previous *ngIf syntax did not.

Let's delve deeper into this syntax.

The @if syntax

Here is a basic example illustrating its usage:

@Component({
  template: `
    @if (showHello) {
    <h2>Hello</h2>
    }
  `,
})
class Test {
  showHello: boolean = true;
}

The code shown will only display the <h2>Hello</h2> element when the showHello property evaluates to true.

Notice how closely this syntax mirrors JavaScript's own if statement.

if (showHello) {
  return `<h2>Hello</h2>`;
}

The resemblance is striking!

Implementing @if with else

Unlike the earlier *ngIf, the @if syntax also supports else conditions.

Consider this example:

@Component({
  template: `
    @if (showHello) {
    <h2>Hello</h2>
    } 
    @else {
    <h2>Goodbye</h2>
    }
  `,
})
class Test {
  showHello: boolean = true;
}

This code will output:

  • the <h2>Hello</h2> element when the showHello property is true
  • the <h2>Goodbye</h2> element when the showHello property is false

This closely replicates the if-else logic found in JavaScript.

Using @if with else if and else

Angular has gone a step further by including support for else if conditions, a capability the previous *ngIf syntax lacked.

Here's an example:

@Component({
  template: `
    @if (showHello) {
      <h2>Hello</h2>
    } 
    @else if (showGoodbye) {
      <h2>Goodbye</h2>
    } 
    @else {
      <h2>See you later</h2>
    }
  `,
})
class Test {
  showHello: boolean = true;
  showGoodbye: boolean = false;
}

The code above will:

  • render the <h2>Hello</h2> element when showHello is true
  • display the <h2>Goodbye</h2> element when showGoodbye is true and showHello is false
  • display the <h2>See you later</h2> element when both showHello and showGoodbye are false.

Again, this syntax is remarkably similar to what one would write in plain JavaScript.

You can see the new @if syntax in action in this video from our YouTube channel:

The reason @if requires no import

You might have noticed that there's no longer a need to import the @if directive from @angular/common in your component templates.

This is because @if is integrated into the template engine itself and is not a directive.

The new @if is a native part of the template engine, making it automatically accessible in every template.

It works similarly to the {{variable}} interpolation or the i18n syntax—no imports are necessary.

The reason the * syntax is obsolete

The * syntax was necessary solely because ngIf was a structural directive. It served as a convenient shorthand to simplify the usage of structural directives for developers.

You can find more details about how the old * syntax operated internally here.

The key point is that with @if, the * syntax is no longer needed, as @if is not a structural directive.

What makes the new @if syntax superior to ngIf?

Let's recap the key advantages of @if over *ngIf:

  • more concise and intuitive
  • eliminates the need for imports
  • supports else if and else clauses
  • incurs no runtime cost
  • paves the way for easier future framework evolution

How to migrate to the new @if syntax?

For those on earlier Angular versions, transitioning to the new @if syntax is possible via the Angular CLI.

The Angular CLI provides an automated migration tool to update your codebase to the new @if syntax:

ng generate @angular/core:control-flow

Executing this command will convert all *ngIf directives in your project to the new syntax, covering not only @if but also @for and @switch.

Comparing @if with CSS-based element hiding

In HTML, elements can be hidden by adjusting their display and visibility properties:

  • setting the display property of an element to none removes it from the visual layout
  • setting the visibility property to hidden prevents the element from being displayed.

Although these methods might appear to achieve the same result, they are fundamentally different.

In each of these CSS-based scenarios, the element remains in the DOM, whereas with @if, the concealed element is entirely absent from the DOM.

The anti-pattern of multiple nested @if with the async pipe

Remember the anti-pattern involving @if we mentioned at the start?

If you incorporate RxJs in your projects, this is a particularly relevant point, as you're likely to encounter it frequently. It's directly connected to @if.

This issue stems from using the @if syntax not for toggling visibility, but solely for extracting values from an observable via the async pipe.

This practice is sometimes referred to as "async pipe chaining" or the "pyramid of doom".

Here's an example of this @if anti-pattern:

@if (user$ | async; as user) {
    ....
    @if (course$ | async; as course) {
        ....
        @if (lessons$ | async; as lesson) {
            ....
        }
    }
}

As shown, we're using the async pipe to unwrap values from several Observables and assigning them to local template variables.

These local variables are then used in subsequent @if blocks.

We're repeating the @if and async pipe combination at multiple template nesting levels, purely for data access, with no other purpose.

This method leads to code that is harder to read and maintain as nesting depth increases.

Furthermore, it complicates refactoring if such patterns are scattered across different parts of the page rather than being centralized.

A more effective approach is to refactor your component to expose a single data$ observable which contains all the requisite data.

Start by defining an interface for all page data:

interface PageData {
    user: User;
    course: Course;
    lessons: Lesson[];
}

Next, define a unified data$ observable that holds everything the page needs:

@Component
export class Component implements OnInit {
    
  private data$: Observable<PageData>;

  ngOnInit() {
    
    const user$ = // ... initialize user$ observable

    const course$  = // ... initialize course$ observable

    const lessons$ = // ... initialize lessons$ observable    
    
    this.data$ = combineLatest([user$, course$, lessons$])
      .pipe(
        map(([user, course, lessons]) => {
          return {
                user, 
                course, 
                lessons
            }
        })
    );
  }
    
}

The combineLatest operator is just one example of how to construct this combined Observable.

Finally, employ @if alongside the async pipe to extract the value of data$ in your template:

@if (data$ | async; as data) {
    ....
    {{data.course}}

    {{data.user}}

    {{data.lessons}}
}  

This approach eliminates the unnecessary @if nesting and results in significantly more readable and maintainable code.

By applying this pattern at the top of your template, you can easily access all your data throughout the rest of it.

Looking to learn more?

To stay informed when new articles like this one are published, we invite you to subscribe to our newsletter:

You'll also receive the latest news about the Angular ecosystem.

For a deep dive into all the core features of Angular like @if, consider exploring the Angular Core Deep Dive Course:

Angular @if: Complete Guide — figure 1

Summary

This guide examined the new @if syntax in Angular. We learned that it offers a more concise and readable format, requires no imports, and supports else if and else branches, unlike the older *ngIf syntax.

With @if, there's no longer a need for the NgIf directive or the sometimes awkward structural * syntax.

The @if syntax represents a valuable new capability in the Angular ecosystem. So go ahead and experiment with this new control flow!

Run the Angular CLI control flow migration on your application and share your experience in the comments section below.

If you have any questions or feedback, please don't hesitate to let us know. We're here to help!

AU
Angular University

Writes about RxJS, Components, Signals. Active 2015–2026.

All 79 articles →