---
name: angular-20-control-flow
description: Angular 20 built-in control flow syntax (@if, @for, @switch, @defer) for modern template programming. Use when writing templates with conditional rendering, loops, switch statements, or lazy loading components. Replaces *ngIf, *ngFor, *ngSwitch with new block syntax for better performance and type safety.
license: MIT
---
# Angular 20 Control Flow Skill
## Rules
### Control Flow Syntax
- Use `@if` / `@else` / `@else if` for conditional rendering
- Use `@for` with mandatory `track` expression for list iteration
- Use `@switch` / `@case` / `@default` for multi-branch conditionals
- Use `@defer` for lazy loading and code splitting
- MUST NOT use structural directives: `*ngIf`, `*ngFor`, `*ngSwitch`
### @for Track Expression
- Every `@for` loop MUST include a `track` expression
- Track by unique ID: `track item.id`
- Track by index for static lists: `track $index`
- MUST NOT track by object reference
### @defer Loading States
- Use appropriate trigger: `on viewport`, `on interaction`, `on idle`, `on immediate`, `on timer(Xs)`, `on hover`
- Use `@loading (minimum Xms)` to prevent UI flashing
- Use `@placeholder (minimum Xms)` for minimum display time
### Signal Integration
- Control flow conditions MUST use signal invocation: `@if (signal())`
- MUST NOT use plain properties without signal invocation
### Context Variables
- Available in `@for`: `$index`, `$first`, `$last`, `$even`, `$odd`, `$count`
---
## Context
### Purpose
This skill provides comprehensive guidance on **Angular 20's built-in control flow syntax**, which introduces new template syntax (@if, @for, @switch, @defer) that replaces structural directives with better performance, type safety, and developer experience.
### What is Angular Control Flow?
Angular 20 introduces new built-in control flow syntax:
- **@if / @else**: Conditional rendering (replaces *ngIf)
- **@for**: List iteration with tracking (replaces *ngFor)
- **@switch / @case**: Multi-branch conditionals (replaces *ngSwitch)
- **@defer**: Lazy loading and code splitting (new feature)
- **@empty**: Fallback for empty collections
- **@placeholder / @loading / @error**: Defer states
### When to Use This Skill
Use Angular 20 Control Flow when:
- Writing templates with conditional rendering
- Iterating over lists or arrays
- Implementing switch/case logic in templates
- Lazy loading components or content blocks
- Handling loading states and error boundaries
- Optimizing bundle size with deferred loading
- Migrating from *ngIf, *ngFor, *ngSwitch to modern syntax
### Core Control Flow Blocks
#### 1. @if - Conditional Rendering
**Basic Usage:**
```typescript
@Component({
template: `
@if (isLoggedIn()) {
Welcome back, {{ username() }}!
}
`
})
export class WelcomeComponent {
isLoggedIn = signal(false);
username = signal('User');
}
```
**@if with @else:**
```typescript
@Component({
template: `
@if (user()) {
} @else {
}
`
})
export class AppComponent {
user = signal(null);
}
```
**@if with @else if:**
```typescript
@Component({
template: `
@if (status() === 'loading') {
} @else if (status() === 'error') {
} @else if (status() === 'success') {
} @else {
}
`
})
export class DataComponent {
status = signal<'loading' | 'error' | 'success' | 'idle'>('idle');
errorMessage = signal('');
data = signal([]);
}
```
**Type Narrowing:**
```typescript
@Component({
template: `
@if (item(); as currentItem) {
@for (item of items(); track item.id; let idx = $index, first = $first, last = $last) {
{{ idx + 1 }}.{{ item.name }}
}
`
})
export class IndexedListComponent {
items = signal([]);
}
```
**Available Context Variables:**
- `$index` - Current index (0-based)
- `$first` - True if first item
- `$last` - True if last item
- `$even` - True if even index
- `$odd` - True if odd index
- `$count` - Total number of items
**@for with @empty:**
```typescript
@Component({
template: `
@for (product of products(); track product.id) {
} @empty {
No products available
}
`
})
export class ProductListComponent {
products = signal([]);
}
```
**Track By Best Practices:**
```typescript
// ✅ Good - Track by unique ID
@for (user of users(); track user.id) {
}
// ✅ Good - Track by index for static lists
@for (tab of tabs; track $index) {
}
// ❌ Bad - Track by object reference (will cause unnecessary re-renders)
@for (item of items(); track item) {