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));
- *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.
Exercise: Angular Conditional Rendering
Which directive conditionally includes or excludes a template block?