Angular released v22 on June 3, 2026. And this one feels different.
This isn't another incremental update. Angular 22 is a consolidation release — the experiments are over. Signal Forms, Zoneless architecture, and OnPush change detection are all production-stable now. Years of work have converged into a single version that genuinely changes how you build Angular apps.
Let's break down every major feature you need to know.
OnPush Is Now the Default Change Detection Strategy
A decision that has been discussed in the Angular community since the very beginning is now reality: OnPush is the new default change detection strategy. Medium
This makes perfect sense in a Signals-first world. Anyone using Signals gets precise notifications about changes, and OnPush takes full advantage of that — focusing change detection only on the components actually affected.
// If you need the old behavior,you''ll set it explicitly
@Component({
selector: 'app-legacy',
changeDetection: ChangeDetectionStrategy.Eager, // replaces the old 'Default'
template: `...`
})
export class LegacyCmp {}
Note: the old Default strategy is now deprecated and replaced by Eager. And don't worry about breaking changes — ng update automatically sets Eager on your existing components during migration if OnPush was not explicitly set.
Signal Forms — Finally Stable
Signal Forms are now stable and ready for production use. The path from experimental to stable was remarkably fast, made possible by extensive internal case studies at Google.
The heart of Signal Forms is the form function:
import { linkedSignal } from '@angular/core';
import { form, minLength, required } from '@angular/forms/signals';
@Component({ [...] })
export class FlightEdit {
private readonly store = inject(FlightDetailStore);
protected readonly flight = linkedSignal(() =>
normalizeFlight(this.store.flight())
);
protected readonly flightForm = form(this.flight, (path) => {
required(path.from);
required(path.to);
minLength(path.from, 3);
});
}
The result is a FieldTree — a deeply nested Signal structure where each property is represented as a Signal with full form status (value, dirty, invalid, errors). Bind it in your template with the FormField directive:
<input [formField]="flightForm.from" id="flight-from" />
<div>{{ flightForm.from().errors() | json }}</div>
No more verbose FormGroup and FormControl boilerplate. This is forms the Angular way in 2026.
Resource API — Stable and Production-Ready
The Resource API and Signal Forms are now stable and ready for production use. The resource, rxResource, and httpResource functions can all be safely used in production.
The most convenient entry point is httpResource:
import { httpResource } from '@angular/common/http';
@Component({ [...] })
export class FlightSearch {
protected readonly filter = signal({ from: 'Hamburg', to: 'Graz' });
protected readonly flightsResource = httpResource<Flight[]>(
() => ({
url: 'https://api.example.com/flights',
params: {
from: this.filter().from,
to: this.filter().to,
},
}),
{ defaultValue: [] }
);
protected readonly flights = this.flightsResource.value;
protected readonly error = this.flightsResource.error;
protected readonly isLoading = this.flightsResource.isLoading;
}
And the template reacts automatically:
@if (flightsResource.isLoading()) {
<div>Loading...</div>
}
@for (flight of flightsResource.value(); track flight.id) {
<app-flight-card [item]="flight" />
}
Race conditions are handled automatically — only the most recent request result is used, matching the behavior of switchMap in RxJS.
The New @Service Decorator
One of the most elegant additions in Angular 22. The new @Service decorator replaces the common @Injectable({ providedIn: 'root' }) pattern with something more intentional:
import { Service } from '@angular/core';
// Provided in root by default
@Service()
export class FlightClient { }
// Opt out of auto-providing
@Service({ autoProvided: false })
export class TabRegistry { }
injectAsync — Lazy Dependency Injection
A brand new primitive: injectAsync lets you inject dependencies lazily — only when they're actually needed. This is particularly powerful for heavy services, feature-specific dependencies, or anything you don't want loaded at startup.
@Component({ [...] })
export class ReportComponent {
private getReportService = injectAsync(() =>
import('./report.service').then(m => m.ReportService)
);
async generateReport() {
const service = await this.getReportService();
service.generate();
}
}
Combined with lazy-loaded routes, this gives you incredibly fine-grained control over what gets loaded and when.
debounced() Signals
A small but very practical addition — debounced() wraps a signal and delays its notifications, perfect for search inputs and other high-frequency interactions:
const searchTerm = signal('');
const debouncedTerm = debounced(searchTerm, 300); // 300ms delay
// Use debouncedTerm in httpResource to avoid firing a request on every keystroke
protected readonly results = httpResource(() => ({
url: '/api/search',
params: { q: debouncedTerm() }
}));
No more manual debounceTime in RxJS pipelines for this common pattern.
Incremental Hydration On by Default
Angular 22 is also a serious security release, and most of the fixes apply automatically. The platform-server package guards against server-side request forgery (SSRF) and path hijacking, rejects suspicious and protocol-relative URLs, and closes SSRF bypasses through backslash URLs in HttpClient.
On the SSR side, provideClientHydration() now enables Incremental Hydration automatically. If you don't need it, opt out explicitly with withNoIncrementalHydration().
Angular AI Tooling
Angular 22 announced Angular Aria is stable, and AI tooling has arrived. The Angular MCP Server — introduced in Angular 21 — continues to mature, enabling AI-assisted development workflows directly in tools like VS Code and Cursor. Think automatic migrations, intelligent code generation, and AI-powered refactoring that understands Angular's architecture.
Top comments (0)