Skip to content
October 23, 2026 Practical guides on software, AI and security

Search Shartech Blogs

Software Development

Angular 22 Upgrade Guide: Breaking Changes, What’s New Through 22.2, and the New Yearly Release Cycle

Table of Contents

Angular 22 shipped on 3 June 2026, and in most years that would make it old news by October, because Angular 23 would be a few weeks away. This year it isn’t. The Angular team has moved to one major release a year, so v22 is the version you will live with until roughly June 2027. Meanwhile two feature releases, 22.1 in late July and 22.2 in the week of 21 September, have added things that were missing from 22.0.

That changes the upgrade calculation. If you were planning to skip 22 and jump to 23 in November, there is no 23 in November. And if you are still on Angular 20, its long-term support ends on 28 November 2026. This guide covers the new schedule, the breaking changes that actually bite during the upgrade, and the features from 22.0 to 22.2 worth adopting.

The new release cycle in one table

The official versioning and releases page now says Angular ships “a major release every 12 months” with “4-6 minor releases for each major release”. Until v22, the cadence was a major every six months with one to three minors. Support has also grown: each major gets 12 months of active support and 12 months of LTS, so 24 months in total instead of 18. Deprecated APIs now stay for a minimum of one major version, which is still about a year.

Version Status (October 2026) Released Active support ends LTS ends
22.x Active 3 June 2026 June 2027 June 2028
21.x LTS 19 Nov 2025 3 June 2026 June 2027
20.x LTS 28 May 2025 19 Nov 2025 28 Nov 2026

The published schedule lists 22.3 around November 2026, 22.4 around January 2027, 22.5 around March 2027 and 23.0 around June 2027. These are approximate dates and the team says they may change.

For teams, the practical effect is simpler planning: one breaking upgrade per year, at a predictable time, with new features arriving in minors that do not break your code. The trade-off is that features which need a breaking change now wait up to a year.

Before you upgrade: version requirements

According to the version compatibility table, Angular 22.0.x needs:

  • Node.js ^22.22.3, ^24.15.0 or ^26.0.0. Node 20 is no longer on the list.
  • TypeScript >=6.0.0 <6.1.0. TypeScript 5.x is not supported, and neither is TypeScript 7 with its new Go-based compiler.
  • RxJS ^6.5.3 or ^7.4.0.

Check your CI images as well as your laptop. A pipeline still pinned to Node 20 is an easy way for an otherwise clean upgrade to fail on its first push.

Running the upgrade

Angular upgrades go one major at a time, so a v20 app goes to 21 first and then to 22. Start from a clean git working tree so you can review exactly what the migrations changed:

git switch -c chore/angular-22
ng update @angular/core@21 @angular/cli@21   # only if you are on v20
ng update @angular/core@22 @angular/cli@22
# if you use Angular Material / CDK:
ng update @angular/material@22

The interactive update guide generates a checklist for your starting version and app complexity. Read it, then read the diff that ng update produced. Several migrations in v22 deliberately preserve old behaviour by adding code, and you will want to know where that code went.

The breaking changes that matter most

The full list is in the v22.0.0 release notes. These are the ones most apps will notice.

OnPush is now the default, and Default is now Eager

Components without a changeDetection setting now use OnPush. The old strategy has been renamed from ChangeDetectionStrategy.Default to ChangeDetectionStrategy.Eager. The migration protects existing code by adding Eager to every component that had no explicit strategy:

@Component({
  selector: 'app-order-list',
  templateUrl: './order-list.html',
  changeDetection: ChangeDetectionStrategy.Eager, // added by ng update
})
export class OrderList {}

Nothing breaks on day one, but every new component you generate will be OnPush. A component that mutates arrays in place or relies on a setTimeout to refresh the view will look frozen until it uses signals or immutable updates. Over time, removing the Eager lines one component at a time is a good way to move towards a fully signal-driven app.

HttpClient uses Fetch by default

FetchBackend is now the default backend, and withFetch() is deprecated because it no longer does anything. The catch is upload progress: the Fetch API cannot report it. If you show progress bars for file uploads, keep the XHR backend:

import { provideHttpClient, withXhr } from '@angular/common/http';

export const appConfig = {
  providers: [provideHttpClient(withXhr())],
};

The migration removes withFetch() calls that are now redundant, and adds withXhr() to apps that were using the XHR backend so their behaviour doesn’t change. The reportProgress option is also deprecated in favour of separate reportUploadProgress and reportDownloadProgress options.

Router: inherited params and canMatch

Two router changes can alter behaviour without a compile error. First, paramsInheritanceStrategy now defaults to 'always', so child routes see their parents’ params and data. According to Ninja Squad’s 22.0 write-up there is no automatic migration, so if your code depended on the old behaviour, restore it explicitly:

provideRouter(routes, withRouterConfig({ paramsInheritanceStrategy: 'emptyOnly' }))

Second, CanMatchFn and class-based canMatch guards now receive a required third argument, currentSnapshot. A migration updates function guards; check class implementations by hand. provideRoutes() has also been removed in favour of provideRouter().

Templates are stricter

  • Optional chaining in templates now returns undefined instead of null, matching TypeScript. The migration wraps existing ?. expressions in $safeNavigationMigration() to keep the old result. Search for it afterwards and remove the wrapper wherever null versus undefined doesn’t matter.
  • strictTemplates is on by default for new projects. ng update writes an explicit setting into your tsconfig so an existing project keeps compiling the way it did.
  • An element matched by two components is now a compile-time error, as is binding the same input or output twice through an alias.
  • data- prefixed attributes no longer bind to inputs or outputs.

Removed APIs

ComponentFactoryResolver, ComponentFactory, createNgModuleRef and the Hammer.js integration are gone. If an old library still calls ComponentFactoryResolver, it will not run on v22, so check your third-party dependencies before you start.

What’s new in 22.0 worth adopting

Signal Forms are stable

Signal Forms, introduced as experimental in v21, are now production-ready. The model is a plain signal, validation is a schema function, and inputs bind with the FormField directive. This is the shape of the example in the official essentials guide:

import { Component, signal } from '@angular/core';
import { email, form, FormField, required } from '@angular/forms/signals';

@Component({
  selector: 'app-login',
  imports: [FormField],
  template: `
    <input type="email" [formField]="loginForm.email" />
    @if (loginForm.email().touched() && loginForm.email().invalid()) {
      @for (error of loginForm.email().errors(); track error) {
        <p>{{ error.message }}</p>
      }
    }
  `,
})
export class Login {
  loginModel = signal({ email: '', password: '' });

  loginForm = form(this.loginModel, (path) => {
    required(path.email, { message: 'Email is required' });
    email(path.email, { message: 'Enter a valid email address' });
    required(path.password, { message: 'Password is required' });
  });
}

Existing reactive and template-driven forms keep working, and 22.0 even lets custom Signal Forms controls work inside them, so there is no need to rewrite working forms. Use Signal Forms for new ones. If you tried the experimental version in v21, note that touched is no longer a model on custom controls; they now use a touched input and a touch() output, and you have to change that by hand.

Resources are stable, and can depend on each other

resource(), rxResource() and httpResource() are no longer experimental. A new chain() helper lets one resource wait for another, and the dependent resource mirrors the source’s loading and error states:

const postsResource = httpResource(({ chain }) => {
  const user = chain(userResource);
  return user ? `/api/posts?authorId=${user.authorId}` : undefined;
});

@Service and injectAsync

The new @Service() decorator, imported from @angular/core, is a shorter replacement for @Injectable({ providedIn: 'root' }). It only supports inject(), not constructor injection, so keep @Injectable for services that need constructor parameters, non-root scopes or advanced provider options. ng generate service now produces @Service(), and since 22.1 the ng generate @angular/core:service schematic converts eligible existing services.

injectAsync() loads a service through a dynamic import, so a heavy service, such as one that pulls in a PDF or charting library, lands in its own lazy chunk:

private readonly reportService = injectAsync(
  () => import('./report.service').then((m) => m.ReportService),
);

Smaller quality-of-life changes

  • @switch can share one block between several @case labels, and @default never; turns a missed union member into a compile error.
  • Templates accept comments inside element tags, spread syntax and short arrow functions.
  • @defer (on idle(500ms)) adds a timeout to idle triggers.
  • Incremental hydration is now the default for server-side rendering.
  • Angular Aria (@angular/aria) is generally available with twelve accessible UI patterns.
  • Webpack-based builders (@angular-devkit/build-angular and @ngtools/webpack) are deprecated. If you are still on them, moving to the esbuild-based application builder should be on your 2027 plan.

What 22.1 and 22.2 added

Because minors now carry the new features, the v22 line keeps growing. The main additions so far:

  • 22.1: linkedSignal accepts a custom set function; JSONP support is deprecated; Rolldown became stable and the default for production chunk optimisation; the CLI’s MCP tools for running builds and the dev server became stable for AI coding agents.
  • 22.2: the @boundary block (developer preview), router resources (developer preview), RedirectCommand can be thrown from guards and resolvers, private members can be used in a component’s own template, type-checking now runs in parallel with bundling, and the test runner moved to Vitest v5.

The @boundary block fills a long-standing gap. It catches errors thrown while rendering a part of the template and shows fallback content, much like a React error boundary:

@boundary {
  <app-sales-chart [data]="sales()" />
} @error {
  <p>The chart could not be displayed.</p>
  <button (click)="$reset()">Try again</button>
}

Router resources, enabled with withRouterResources(), are a signal-based alternative to resolvers that load data for all matched routes in parallel. Both features are in developer preview, so the API can still change. They are fine to try on non-critical screens; wait for them to be stable before building core flows on them.

A sensible upgrade plan

  1. Still on v20: upgrade now. LTS ends on 28 November 2026, and you have two majors to cross.
  2. On v21: you are covered until June 2027, but there is no reason to wait for v23. Upgrade to 22 in the next few sprints so you can pick up each minor as it ships.
  3. During the upgrade: bump Node and TypeScript in CI first, run ng update, then review every Eager, withXhr() and $safeNavigationMigration() the migrations added.
  4. Test the router: check routes that read params or data, because the inheritance change will not show up as a compile error.
  5. After the upgrade: write new components as OnPush with signals, new forms as Signal Forms, and new data loading with httpResource. Leave working legacy code alone unless you are already changing it.

My recommendation is to treat v22 as a platform for the next year rather than a one-off upgrade. With majors once a year, the minors are where the new work lands, and staying current on 22.x is now cheap.

Sources: Angular: Versioning and releases, Angular: Version compatibility, Angular Blog: Announcing Angular v22, Angular v22.0.0 release notes (GitHub), Angular: Signal Forms essentials, Angular: Creating and using services, Ninja Squad: What’s new in Angular 22.0, Ninja Squad: What’s new in Angular 22.1, Ninja Squad: What’s new in Angular 22.2

Related reading

Did you find this article helpful?

Written by

Malik Shamir

Malik Shamir is the founder and lead tech writer at SharTech, a modern technology platform focused on artificial intelligence, software development, cloud computing, cybersecurity, and emerging digital trends. With hands-on experience in full-stack development and AI systems, Shamir creates clear, practical, and research-based content that helps readers understand complex technologies in simple terms. His mission is to make advanced tech knowledge accessible, reliable, and useful for developers, entrepreneurs, and digital learners worldwide.

54 Articles Website
Previous Article Prisma vs Drizzle vs TypeORM: Choosing a TypeScript ORM as Prisma 8, Drizzle v1 and TypeORM 1.0 Arrive Next Article Python 3.15 Is Out: Lazy Imports, frozendict, UTF-8 by Default and How to Upgrade

Leave a Comment

Your email address will not be published. Required fields are marked *

Stay Updated with Shartech

Get smart tech insights, tutorials, and the latest in AI & programming directly in your inbox. No spam, ever.

We respect your privacy. Unsubscribe at any time.