Angular Conditional Rendering

Conditional rendering shows or hides parts of a template based on component state, using either the classic *ngIf directive or the newer built-in @if block.

The Classic Approach: *ngIf

For years, *ngIf has been the standard way to conditionally include an element in the rendered DOM. When the bound expression is falsy, Angular removes the element and its children entirely rather than just hiding them with CSS, which means components inside are destroyed and their lifecycle hooks fire accordingly.

*ngIf with an else template

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-order-status',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div *ngIf="isDelivered; else pending">
      Your order has been delivered.
    </div>
    <ng-template #pending>
      <div>Your order is still on its way.</div>
    </ng-template>
  `
})
export class OrderStatusComponent {
  isDelivered = false;
}

bootstrapApplication(OrderStatusComponent).catch((err) => console.error(err));

The Modern Approach: @if

Angular's built-in control flow, introduced as a stable feature in Angular 17, replaces *ngIf with a block syntax that reads closer to plain TypeScript. It supports @else if and @else branches directly, requires no CommonModule import, and gives better type narrowing inside each branch.

@if / @else if / @else block syntax

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-order-status-modern',
  standalone: true,
  template: `
    @if (status === 'delivered') {
      <p>Your order has been delivered.</p>
    } @else if (status === 'shipped') {
      <p>Your order is on the way.</p>
    } @else {
      <p>Your order is being prepared.</p>
    }
  `
})
export class OrderStatusModernComponent {
  status: 'preparing' | 'shipped' | 'delivered' = 'shipped';
}

bootstrapApplication(OrderStatusModernComponent).catch((err) => console.error(err));

Narrowing with @if and an alias

One advantage @if has over *ngIf is the ability to alias the checked expression with the as keyword, which is especially useful when rendering data from an async pipe or a nullable value, since the alias is guaranteed non-null inside the block.

Aliasing a value inside @if

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

interface User {
  name: string;
}

@Component({
  selector: 'app-user-banner',
  standalone: true,
  template: `
    @if (currentUser; as user) {
      <p>Welcome back, {{ user.name }}!</p>
    } @else {
      <p>Please log in.</p>
    }
  `
})
export class UserBannerComponent {
  currentUser: User | null = { name: 'Sam' };
}

bootstrapApplication(UserBannerComponent).catch((err) => console.error(err));
Feature*ngIf@if
Requires CommonModule importYesNo
Else branch syntax*ngIf="cond; else tpl" + <ng-template>@else { ... } inline
Multiple branchesNested ternary or ngSwitch needed@else if chains directly
Type narrowing in branchLimitedFull TypeScript narrowing
  • *ngIf removes the element from the DOM on false, unlike [hidden] which only toggles CSS display.
  • @if blocks are compiled directly by the Angular compiler and do not need any directive import.
  • Use the ; as alias syntax with @if to safely unwrap a value that might be null or undefined.
  • For more than two branches, @switch is usually clearer than a long @else if chain.
Note: Both *ngIf and @if are functionally equivalent for simple cases — new Angular projects are encouraged to prefer @if, but *ngIf remains fully supported and you will see it often in existing codebases.
Note: Because *ngIf destroys and recreates DOM nodes, any local component state inside the conditional block resets every time the condition flips back to true — use @if the same way, since the destroy-and-recreate behavior is identical.

Exercise: Angular Conditional Rendering

Which directive conditionally includes or excludes a template block?