Skip to content

Angular 18: Latest Features and What's New

Discover the latest features in Angular 18, including improved Signals, new control flow syntax, and enhanced developer experience.

By 5 min read
  • Angular
  • Angular 18
  • TypeScript
  • Frontend
  • Web Development
Angular 18: Latest Features and What's New

Angular 18 brings significant improvements to the framework, building on the foundation laid by Signals in Angular 16. After working with Angular 18 in production applications like Danone BAPEN and Ship-Watch, I've experienced firsthand how these features improve developer productivity and application performance.

New Control Flow Syntax

Angular 18 introduces a new control flow syntax that's more intuitive and performant than *ngIf and *ngFor.

@if, @else, @for

TSX
// Old syntax
<div *ngIf="user; else noUser">
  {{ user.name }}
</div>
<ng-template #noUser>No user</ng-template>

// New syntax
@if (user) {
  <div>{{ user.name }}</div>
} @else {
  <div>No user</div>
}

// @for with trackBy built-in
@for (item of items; track item.id) {
  <div>{{ item.name }}</div>
} @empty {
  <div>No items</div>
}

// @switch
@switch (status()) {
  @case ('loading') {
    <div>Loading...</div>
  }
  @case ('success') {
    <div>Success!</div>
  }
  @default {
    <div>Unknown status</div>
  }
}

Improved Signals API

Signals get enhanced with better type safety and new utilities:

TSX
import { Component, signal, computed, effect, input, output } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  template: `
    <div>
      <h2>{{ displayName() }}</h2>
      <p>{{ user().email }}</p>
      <button (click)="onEdit.emit()">Edit</button>
    </div>
  `,
})
export class UserProfileComponent {
  // Required input signal
  user = input.required<User>();

  // Optional input with default
  showEmail = input<boolean>(true);

  // Output signal
  onEdit = output<void>();

  // Computed from input signals
  displayName = computed(() => {
    const user = this.user();
    return `${user.firstName} ${user.lastName}`;
  });
}

Material 3 Support

Angular Material now supports Material Design 3:

TSX
import { provideMaterial3 } from '@angular/material';

bootstrapApplication(AppComponent, {
  providers: [
    provideMaterial3(), // Enable Material 3
  ],
});

Zoneless Change Detection (Experimental)

Angular 18 introduces experimental support for zoneless change detection using Signals:

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

bootstrapApplication(AppComponent, {
  providers: [
    provideExperimentalZonelessChangeDetection(),
  ],
});

Improved Standalone Components

Standalone components get better support and tooling:

TSX
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-standalone',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <div>
      <input [(ngModel)]="name" />
      <p>Hello, {{ name() }}!</p>
    </div>
  `,
})
export class StandaloneComponent {
  name = signal<string>('');
}

New Lifecycle Hooks

Angular 18 introduces new lifecycle hooks for better control:

TSX
import { Component, afterNextRender, afterRender } from '@angular/core';

@Component({
  selector: 'app-chart',
  template: '<canvas #chart></canvas>',
})
export class ChartComponent {
  // Runs after next render
  constructor() {
    afterNextRender(() => {
      // Initialize chart library
      this.initChart();
    });
  }

  // Runs after every render
  ngAfterRender() {
    // Update chart
  }

  private initChart(): void {
    // Chart initialization
  }
}

Improved Server-Side Rendering

Better SSR support with hydration improvements:

TSX
import { provideClientHydration } from '@angular/platform-browser';

bootstrapApplication(AppComponent, {
  providers: [
    provideClientHydration({
      // Improved hydration options
    }),
  ],
});

Enhanced Forms API

Improved reactive forms with better type safety:

TSX
import { Component, signal } from '@angular/core';
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';

interface UserForm {
  name: string;
  email: string;
}

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()">
      <input formControlName="name" />
      <input formControlName="email" />
      <button [disabled]="!form.valid">Submit</button>
    </form>
  `,
})
export class UserFormComponent {
  form: FormGroup<UserForm>;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
    });
  }

  onSubmit(): void {
    if (this.form.valid) {
      console.log(this.form.value);
    }
  }
}

Improved Dependency Injection

Better DI with functional providers:

TSX
import { provideHttpClient } from '@angular/common/http';
import { provideRouter } from '@angular/router';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(),
    provideRouter(routes),
    // Functional providers are cleaner
  ],
};

Performance Improvements

Angular 18 includes several performance enhancements:

TSX
// Automatic OnPush optimization with Signals
@Component({
  selector: 'app-optimized',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div>{{ count() }}</div>
  `,
})
export class OptimizedComponent {
  count = signal<number>(0);
  // Only re-renders when count changes
}

Migration Guide

Migrating to Angular 18:

  1. Update dependencies: ng update @angular/core @angular/cli
  2. Replace control flow: Migrate *ngIf to @if, *ngFor to @for
  3. Adopt Signals: Gradually migrate state to signals
  4. Update Material: Use Material 3 if desired
  5. Test thoroughly: Ensure all features work correctly

Real-World Example

Complete example using Angular 18 features:

TSX
import { Component, signal, computed, input, output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

interface Product {
  id: string;
  name: string;
  price: number;
  inStock: boolean;
}

@Component({
  selector: 'app-product-list',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <input 
      [(ngModel)]="searchTerm"
      (ngModelChange)="searchTerm.set($event)"
      placeholder="Search products"
    />
    
    @if (loading()) {
      <div>Loading...</div>
    } @else if (error()) {
      <div>Error: {{ error() }}</div>
    } @else {
      @for (product of filteredProducts(); track product.id) {
        <div class="product-card">
          <h3>{{ product.name }}</h3>
          <p>${{ product.price }}</p>
          @if (product.inStock) {
            <button (click)="onAddToCart.emit(product.id)">
              Add to Cart
            </button>
          } @else {
            <span>Out of Stock</span>
          }
        </div>
      } @empty {
        <div>No products found</div>
      }
    }
  `,
})
export class ProductListComponent {
  products = input.required<Product[]>();
  loading = input<boolean>(false);
  error = input<string | null>(null);
  onAddToCart = output<string>();

  searchTerm = signal<string>('');

  filteredProducts = computed(() => {
    const term = this.searchTerm().toLowerCase();
    return this.products().filter(product =>
      product.name.toLowerCase().includes(term)
    );
  });
}

Key Benefits

Angular 18 provides:

  • More intuitive control flow syntax
  • Better performance with Signals
  • Improved developer experience
  • Better type safety throughout
  • Enhanced SSR capabilities
  • Material Design 3 support

Angular 18 represents a significant step forward, making Angular more modern, performant, and developer-friendly. The new control flow syntax alone makes templates more readable, while Signals provide a powerful foundation for reactive applications.

Keep reading