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 updatemigration addsChangeDetectionStrategy.Eagerautomatically 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:
validateHttpandvalidateAsyncnow 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:
SimpleComboboxhas been promoted toCombobox. Thesimple-comboboxprefix is gone - update selectors and tokens to theircomboboxequivalents (for example,SIMPLE_COMBOBOX_POPUP→COMBOBOX_POPUP). The legacy combobox and autocomplete implementations were removed.- The
valuesinput/model was renamed tovalueacross Combobox, Listbox, Tree, Menu, Toolbar, and Select. Update your templates to bindvalueinstead ofvalues.
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
undefinedin 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. Adata-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:
paramsInheritanceStrategynow 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
browserUrlinput, 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.
ComponentFactoryResolverandComponentFactoryare gone. Pass the component class directly toViewContainerRef.createComponentor use the standalonecreateComponentfunction.createNgModuleRefremoved usecreateNgModule.ChangeDetectorRef.checkNoChangesremoved in tests, usefixture.detectChanges().- Form
min/maxvalidators no longer accept strings bound values must be numbers ornull.
How to upgrade
The CLI does the heavy lifting and runs the migrations described above automatically:
ng update @angular/core@22 @angular/cli@22Run 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



