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.
- Angular
- Angular 18
- TypeScript
- Frontend
- Web Development
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
// 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:
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:
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:
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:
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:
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:
import { provideClientHydration } from '@angular/platform-browser';
bootstrapApplication(AppComponent, {
providers: [
provideClientHydration({
// Improved hydration options
}),
],
});
Enhanced Forms API
Improved reactive forms with better type safety:
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:
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:
// 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:
- Update dependencies:
ng update @angular/core @angular/cli - Replace control flow: Migrate
*ngIfto@if,*ngForto@for - Adopt Signals: Gradually migrate state to signals
- Update Material: Use Material 3 if desired
- Test thoroughly: Ensure all features work correctly
Real-World Example
Complete example using Angular 18 features:
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
React 19: What's New and Why It Matters
Explore the groundbreaking features in React 19, including Actions, useOptimistic, and the new compiler that transforms how we build React applications.
Essential TypeScript Features Every Frontend Developer Should Master
The most impactful TypeScript features every React, Angular and Vue developer should master, for better code quality and developer experience.
Your Frontend Shouldn't Trust a Single Click
Disabling the button is not enough. Why duplicate-submission protection has to exist on the client and the server, and what each layer is responsible for.