Conditional Rendering in Angular with @if, @else and @switch

Angular Tutorial: Beginner to Advanced · 4. Control flow and lists · 4 min read · Updated

Many screens need to decide what to show: a welcome message for signed-in users and a login prompt for everyone else, a loading spinner while data arrives, an error when something fails. Angular's template control flow handles this with blocks that start with @.

What you will learn

  • How to use @if, @else if and @else
  • How to pick one of several views with @switch
  • How to avoid repeating a condition with an alias

The @if block

Write the condition in parentheses, and put the content in braces:

@if (isSignedIn()) {
  <p>Welcome back!</p>
} @else {
  <p>Please sign in.</p>
}

When the condition is true, Angular adds the content to the page. When it is false, the content is removed completely, which is different from only hiding it with CSS.

You can chain conditions:

@if (score() >= 90) {
  <p>Excellent</p>
} @else if (score() >= 50) {
  <p>Good</p>
} @else {
  <p>Keep practising</p>
}

Choosing between several views with @switch

If one value decides between several views, @switch is clearer than a chain of @if:

@switch (status()) {
  @case ('loading') { <p>Loading…</p> }
  @case ('error') { <p>Something went wrong.</p> }
  @default { <p>All good.</p> }
}

A working example

This component shows tasks, a message when there are none, and a status panel:

// src/app/task.model.ts
export interface Task {
  id: number;
  title: string;
  done: boolean;
}
// src/app/task-list/task-list.ts
import { Component, signal } from '@angular/core';
import { Task } from '../task.model';

type Status = 'loading' | 'ready' | 'error';

@Component({
  selector: 'app-task-list',
  template: `
    <h2>Tasks</h2>

    @switch (status()) {
      @case ('loading') { <p>Loading tasks…</p> }
      @case ('error') { <p role="alert">We could not load your tasks.</p> }
      @default {
        @if (tasks().length > 0) {
          <p>You have {{ tasks().length }} task(s).</p>
        } @else {
          <p>No tasks yet. Enjoy the quiet!</p>
        }
      }
    }

    <button type="button" (click)="setStatus('loading')">Loading</button>
    <button type="button" (click)="setStatus('error')">Error</button>
    <button type="button" (click)="setStatus('ready')">Ready</button>
    <button type="button" (click)="addSample()">Add a task</button>
  `,
})
export class TaskList {
  protected readonly status = signal<Status>('ready');
  protected readonly tasks = signal<Task[]>([]);

  protected setStatus(next: Status): void {
    this.status.set(next);
  }

  protected addSample(): void {
    this.tasks.update((list) => [
      ...list,
      { id: list.length + 1, title: `Task ${list.length + 1}`, done: false },
    ]);
  }
}
// src/app/app.ts
import { Component } from '@angular/core';
import { TaskList } from './task-list/task-list';

@Component({
  imports: [TaskList],
  selector: 'app-root',
  templateUrl: './app.html',
  styleUrl: './app.css',
})
export class App {}
<!-- src/app/app.html -->
<main>
  <h1>Control flow</h1>
  <app-task-list />
</main>

Click the buttons. The status buttons swap the whole panel, and "Add a task" switches the inner @if from the empty message to the count.

Reusing a condition result with an alias

Sometimes the condition is a value you also want to use inside the block. Add ; as name to keep it:

@if (user(); as currentUser) {
  <p>Hello, {{ currentUser.name }}</p>
}

If user() is null, the block is skipped. Inside the block, currentUser is known to be present, so you do not have to check again.

Older syntax

Older Angular code uses *ngIf and *ngSwitch. They still work, but the @ blocks are the current way to write templates, and the CLI can convert old code for you. When you read an older tutorial, translate *ngIf="x" to @if (x) { ... } in your head.

Try it yourself

  1. Add an isAdmin signal and show an "Admin tools" heading only for admins.
  2. Add a button that toggles isAdmin.
  3. Show a different message when there are more than five tasks.

Quick check

  1. What does Angular do with the content of an @if whose condition is false?
  2. When is @switch better than a chain of @if blocks?
  3. How do you keep the value of an @if condition for use inside the block?

Next, show many items at once in Rendering lists with @for.

Frequently asked questions

Is @if better than hiding an element with CSS?

They do different things. @if removes the content, so it also stops any work that content would do. CSS hiding keeps the element in the page. Use @if when the content should not exist at all.

Can I still use *ngIf?

Yes, it still works in current versions, but new code should use @if. You will see *ngIf in older projects.