Rorix Technologies Logo
Engineering21 min read

Angular 22: The Features You'll Actually Use

AngularWebDevelopmentFrontendTypeScriptProgrammingSoftwareEngineeringDevCommunityAngularFrameworkSignalFormsSignalsMCPModelContextProtocolTechBlogAngular22
Angular 22: The Features You'll Actually Use

Angular 22 landed on June 3, 2026, and the release notes are long. Most of it is internal plumbing you'll never touch. This post skips the noise and walks through the handful of changes that actually show up in your day-to-day code in the plain language, with examples.

If you just want the upgrade command, it's at the bottom. Otherwise, let's go.

1. OnPush is the new default

This is the headline change. Every component you write is now OnPush by default. Previously, components ran in the “default” change-detection mode, which re-checks everything on almost any event. OnPush only re-checks when an @Input changes, an event fires inside the component, or a signal it reads updates - which is faster and far more predictable.

In practice, most components don't need anything from you. The ones written with signals and immutable inputs simply get faster for free.

If you have an older component that depends on the old “check everything” behavior, opt back in explicitly:

import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-legacy-widget',
  template: `...`,
  changeDetection: ChangeDetectionStrategy.Eager, // old default behavior
})
export class LegacyWidgetComponent {}

The ng update migration adds ChangeDetectionStrategy.Eager automatically where it thinks you need it, so most upgrades are painless.

2. Signal Forms are officially stable

Signal Forms have graduated from experimental to the public API. If you've been waiting for the “new way” to do forms before adopting it, the wait is over, it's now safe to use in production.

The big additions in this release:

  • validateHttp and validateAsync now take a debounce option, so async checks (like “is this username taken?”) don't fire on every keystroke.
  • reloadValidation() lets you re-run async validation manually.
  • FieldState.getError() gives you direct access to a specific error.

A username field with a debounced server check looks roughly like this:

import { Component, signal } from '@angular/core';
import { form, validateHttp } from '@angular/forms';

@Component({ /* ... */ })
export class SignupComponent {
  model = signal({ username: '' });

  signupForm = form(this.model, (f) => {
    validateHttp(f.username, {
      debounce: 400, // wait 400ms after typing stops
      request: (value) => `/api/check-username?u=${value}`,
      errors: (taken) => (taken ? { kind: 'unavailable' } : null),
    });
  });
}

The point: less RxJS boilerplate, fewer subscriptions to clean up, and validation state you can read like any other signal.

 

3. Angular Aria: accessible UI primitives, now stable

This is a quietly huge one for anyone who cares about accessibility. Angular Aria (the @angular/aria package) has dropped its developer-preview label and is now stable in v22.

So what is it? Angular Aria is a set of headless, accessible UI primitives directives that handle all the hard parts of common interactive patterns (keyboard navigation, focus management, and correct ARIA roles and attributes) while leaving the markup and styling entirely up to you. Think of it as the official Angular answer to “headless” UI libraries: you bring the look, Aria brings the correct, accessible behavior.

It's different from Angular Material. Material gives you styled, opinionated components. Aria gives you unstyled behavior you can wrap around your own design system no fighting someone else's CSS.

The primitives shipping as stable cover the patterns you reach for most:

  • Combobox and Autocomplete
  • Listbox and Select
  • Menu and Toolbar
  • Tabs, Accordion, Tree, and Grid

A listbox shows the idea. You write plain markup, apply the directives, and get full keyboard support (arrow keys, type-ahead, Home/End) plus the right ARIA roles automatically:

<ul ngListbox [(value)]="selectedFruit">
  <li ngOption value="apple">Apple</li>
  <li ngOption value="banana">Banana</li>
  <li ngOption value="cherry">Cherry</li>
</ul>

No role="listbox", no aria-selected bookkeeping, no hand-written keydown handlers the directives wire it all up. You just style the <ul> and <li> however you like.

Two migration notes if you were using the preview version:

  • SimpleCombobox has been promoted to Combobox. The simple-combobox prefix is gone - update selectors and tokens to their combobox equivalents (for example, SIMPLE_COMBOBOX_POPUPCOMBOBOX_POPUP). The legacy combobox and autocomplete implementations were removed.
  • The values input/model was renamed to value across Combobox, Listbox, Tree, Menu, Toolbar, and Select. Update your templates to bind value instead of values.

And because it's stable now, each primitive ships with test harnesses, so you can write reliable tests against accessible behavior straight out of the box.

4. HttpClient uses fetch by default

You no longer need withFetch(). The fetch API is now the default backend, which means better SSR behavior and one less thing to configure. withFetch() still works but is deprecated, you can safely delete it.

// Before (Angular 21)
provideHttpClient(withFetch())

// Angular 22 fetch is already the default
provideHttpClient()

One catch: the fetch backend doesn't report upload progress. If you have file-upload progress bars that depend on that, switch that part back to XHR:

provideHttpClient(withXhr())

Also note the old reportProgress option is deprecated. It's now split into two clearer flags: reportUploadProgress and reportDownloadProgress.

5. Incremental hydration is on by default

If you do server-side rendering, incremental hydration is now the default behavior no withIncrementalHydration() needed. Instead of Angular waking up (“hydrating”) the entire page at once, it hydrates pieces lazily, typically as the user scrolls to them or interacts. The result is a faster, more responsive first load with no extra setup.

6. Signals can now be debounced

A small but genuinely handy addition: you can debounce a signal directly, instead of reaching for RxJS operators every time you want to wait for typing to settle.

import { signal, debounced } from '@angular/core';

search = signal('');
// emits the latest value only after 300ms of quiet
debouncedSearch = debounced(this.search, 300);

Great for search boxes, autosave, and anything where you don't want to react to every single change.

7. New @Service decorator and injectAsync

Two dependency-injection conveniences arrived. The new @Service decorator is a clearer, purpose-named alternative to @Injectable for the common case of a plain service:

import { Service } from '@angular/core';

@Service()
export class CartService {
  // ...
}

And injectAsync lets you pull in a dependency that may be lazily loaded, returning a promise instead of forcing everything to be available up front useful for code-splitting heavy services.

const reporting = await injectAsync(ReportingService);

8. @defer gets finer control over idle loading

Deferrable views could already load “when the browser is idle.” In v22 you get more control over that idle behavior, including an optional timeout so a block doesn't wait forever for an idle moment that never comes.

@defer (on idle) {
  <app-heavy-chart />
} @placeholder {
  <p>Chart loading soon…</p>
}

If your app is always busy, the new idle-timeout option means the deferred content still loads after a sensible delay instead of stalling.

9. Smarter, stricter template type-checking

The template compiler got noticeably more correct, which mostly means it'll catch bugs you couldn't see before. A few that matter:

  • Optional chaining now returns undefined in templates, matching how TypeScript behaves. Safe navigation (?.) also narrows nullable types properly, so the compiler stops complaining about values it can prove are non-null.
  • Duplicate inputs/outputs throw at compile time. If two bindings target the same input, you'll get an error instead of silent weirdness.
  • data- attributes no longer bind inputs or outputs. A data- attribute is now treated as a plain attribute, as it should be.

Heads-up: this stricter checking can surface new warnings on existing projects (the nullishCoalescingNotNullable and optionalChainNotNullable diagnostics). The upgrade migration temporarily disables them in your tsconfig so your build doesn't break on day one but you can re-enable and clean them up at your own pace.

10. Router defaults changed (one to watch)

Two router updates are worth knowing:

  • paramsInheritanceStrategy now defaults to 'always' (it was 'emptyOnly'). Child routes now inherit params from all parents by default. If your app relied on the old behavior, set it back explicitly in your router config.
  • Router links get a browserUrl input, letting you show one URL in the address bar while routing to another handy for vanity URLs and redirects.

Also, the long-deprecated provideRoutes() is gone use provideRouter() instead.

11. The forward-looking part: Angular meets AI tooling

This is the most novel and experimental addition in v22, and the most fun to watch. Angular is starting to ship primitives for exposing parts of your app to AI agents via the Web MCP (Model Context Protocol) standard. New experimental APIs like provideWebMcpTools and declareWebMcpTool let your application register “tools” an AI agent can call, and there are new in-page AI debugging helpers including one that can inspect your dependency-injection graph at runtime.

It's clearly labeled experimental, so don't build production features on it yet. But it signals where the framework is heading: apps that are designed to be operated by agents, not just people. Worth experimenting with in a side project.

Housekeeping that might bite you

A few removals and requirements to check before you upgrade:

  • TypeScript 6.0+ is required. Support for 5.9 and older is dropped.
  • Node.js 26 is supported.
  • Hammer.js integration is removed. If you used gesture support through Angular, you'll need your own implementation.
  • ComponentFactoryResolver and ComponentFactory are gone. Pass the component class directly to ViewContainerRef.createComponent or use the standalone createComponent function.
  • createNgModuleRef removed use createNgModule.
  • ChangeDetectorRef.checkNoChanges removed in tests, use fixture.detectChanges().
  • Form min/max validators no longer accept strings bound values must be numbers or null.

How to upgrade

The CLI does the heavy lifting and runs the migrations described above automatically:

ng update @angular/core@22 @angular/cli@22

Run it on a clean branch, let the schematics do their thing, and review the diff. For the full, authoritative list, see the official “Announcing Angular v22” blog post and the changelogs on GitHub.


 

Ready to Transform Your Warehouse?

Get a free, detailed estimate for your custom WMS solution