Angular 21 landed in late 2025 and continues Angular’s recent “modernization streak”: better reactivity with signals, cleaner template syntax, a sharper performance story, and tooling that feels more current.
This guide is written for real projects—not hello-world demos. You’ll get:
- What’s new in Angular 21 (and why it matters)
- A safe upgrade path (including jumping from older versions)
- Common issues + fixes (zoneless, tests, signals pitfalls)
- Modern template syntax:
@if,@for,@switch,@let,@defer - How to measure performance improvements vs. Angular <18
Along the way, I’ll also link to ngbootstrap—my open-source Angular UI library built for Angular 21 with Bootstrap-friendly styling (Datagrid, Drag & Drop, Stepper, Splitter, Tree, Typeahead, and more). GitHub+2ngbootstrap+2
1) What’s new in Angular 21 (high impact)
Zoneless is the default direction
Angular 21 pushes zoneless change detection as the modern baseline for new apps—less “magic”, more explicit reactivity, and typically less overhead from Zone.js. Angular Blog+2angular.dev+2
Key practical implications:
- You’ll often remove
zone.jsandzone.js/testingfromangular.jsonpolyfills to get full benefit (and avoid warnings). angular.dev+1 - If you still want NgZone behavior (or need
eventCoalescing), you can explicitly opt in. angular.dev
Why it matters: zone-patched async can trigger unnecessary change detection. Zoneless shifts you toward signals + explicit triggers.
Vitest becomes a first-class default testing story
Angular 21 promotes Vitest as the stable, modern unit test runner experience, and provides a schematic to help refactor Jasmine tests.
Migration command:
ng g @schematics/angular:refactor-jasmine-vitest
Signal Forms (experimental)
Angular 21 expands the signals ecosystem with Signal Forms—an experimental, model-driven way to manage forms.
It’s explicitly marked experimental, so treat it as: “try it, learn it, pilot it—don’t bet the whole enterprise app on it yet.”
Developer experience upgrades (templates + tooling)
Modern Angular templates are now much more “semantic”:
@if / @else,@for,@switchcontrol flow.@letfor local template variables.@deferfor deferrable views with improved viewport trigger options (IntersectionObserver options support).
2) Before you upgrade: compatibility and the safe path
Check Node/TypeScript compatibility first
Angular versions are strict about runtime/tooling versions. Confirm your Node.js and TypeScript match Angular 21 requirements before touching code. angular.dev
If you’re jumping multiple majors, upgrade one major at a time
Angular recommends updating across majors step-by-step (ex: 18 → 19 → 20 → 21). angular.dev
If you skip versions, you risk missing migrations and breaking changes.
3) Upgrade steps (works for most real apps)
Step 0 — Prepare the repo
- Create a branch (
upgrade/angular-21) - Ensure tests are green on current version
- Snapshot bundle metrics (see performance section)
Step 1 — Upgrade Angular + CLI (major-by-major)
For each major step (example: 20 → 21):
ng update @angular/core@21 @angular/cli@21
Repeat for each major version if you’re coming from older Angular.
Step 2 — Run the Angular update guide checklist
Use Angular’s update guide to catch version-specific migrations and manual steps. angular.dev
Step 3 — Decide: zoneless or zone-based
Option A (recommended for Angular 21-style apps): Zoneless
import { bootstrapApplication } from '@angular/platform-browser';
import { provideZonelessChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(),
],
});
Then remove Zone.js from polyfills to avoid NG0914 warnings and reduce overhead:
- remove
zone.jsandzone.js/testingfromangular.jsonpolyfills (build + test) angular.dev+1
Option B: Keep NgZone (if you rely on Zone.js behavior)
import { provideZoneChangeDetection } from '@angular/core';
bootstrapApplication(AppComponent, {
providers: [
provideZoneChangeDetection({
eventCoalescing: true,
}),
],
});
provideZoneChangeDetection is also where eventCoalescing lives.
In plain terms,
eventCoalescing: truegroups rapid-fire events so Angular runs fewer change detection passes.
Step 4 — Tests: migrate to Vitest (optional but worth it)
ng g @schematics/angular:refactor-jasmine-vitest
4) Common Angular 21 upgrade issues (and fixes)
Issue A — “Zoneless but still loading Zone.js” (NG0914)
You enabled zoneless change detection, but Zone.js is still included (usually via polyfills). Fix by removing it from angular.json (and polyfills.ts if you import it there).
Issue B — effect() error: “can only be used within an injection context”
This happens when effect() is called outside Angular’s injection context. Fix by moving it into:
- a constructor
- a field initializer
- a function invoked within an injection context
A safe, common pattern is defining effects in the constructor of a component/service that’s already DI-created.
Issue C — Query params “changed but didn’t trigger”
When you switch to signals, it’s easy to accidentally stop listening to router state properly. The robust pattern is:
- keep router streams as Observables
- convert them to signals with
toSignal()(or keep them as streams and derive state)
Example approach (conceptual):
- read
ActivatedRoute.queryParamMap - map to the value you need
distinctUntilChanged()- then trigger your API call
Issue D — Third-party libraries expecting Zone.js
If you rely on libraries that assume zone-patched async behavior, you may need to:
- keep Zone.js (use
provideZoneChangeDetection) - or upgrade/replace the dependency
(Zoneless is great, but the real world has dependencies.)
5) Modern Angular template syntax (Angular 21 “semantic” style)
@if / @else instead of *ngIf
@if (user()) {
<app-profile [user]="user()" />
} @else {
<app-skeleton />
}
@for instead of *ngFor (with clear tracking)
@for (row of rows(); track row.id) {
<li>{{ row.name }}</li>
}
@switch for readable conditional rendering
@switch (status()) {
@case ('loading') { <app-spinner /> }
@case ('error') { <app-error /> }
@default { <app-content /> }
}
@let to reduce repeated pipes and expressions
@let q = (query() ?? '').trim();
@if (q.length > 0) {
<p>Searching for: {{ q }}</p>
}
@defer for real-world lazy UI
@defer can load expensive UI only when it’s needed (example: when scrolled into view). angular.dev
Angular 21 also supports customizing IntersectionObserver options for viewport triggers (useful for preloading before content becomes visible). Angular Blog+1
6) Signals: the practical patterns that avoid bugs
Keep signals “pure” and derived
signal()for statecomputed()for derived valueseffect()for side-effects (API calls, logging, syncing)
Golden rule: don’t mutate state inside a computed.
A clean signal-driven search pattern (pseudo-real)
qis a signal- normalize it
- debounce/throttle (optional)
- call API in an effect and write into another signal
If you’re migrating from RxJS-heavy code, it’s fine to keep RxJS for streams and use signals for UI state.
7) Performance: Angular 21 vs Angular <18 (what changes + how to measure)
What Angular improved since “pre-18”
Angular’s performance story got a real boost starting around v17:
- Built-in control flow (like
@for) reported up to 90% faster runtime in public benchmarks for loops, and major build speed gains (SSR/hybrid builds). Angular Blog
Those wins carry forward into modern Angular (including 21), especially when you adopt the newer syntax and deferrable views.
From v18 onward, zoneless became the big lever:
- Removing Zone.js can reduce overhead and even reduce bundle size when you remove it from polyfills.
“For Angular 21 projects that need Bootstrap-friendly, production-focused components (Datagrid, Drag & Drop, Pagination, Stepper, Splitter), I’ve been building ngbootstrap as a modular, standalone-first library.”
8) Bonus: Add a practical UI upgrade while you’re here (ngbootstrap links)
If your Angular 21 migration includes modernizing UI components (tables, pagination, stepper, builder-style UIs), here are relevant links you can drop inline in your post:
- ngbootstrap GitHub (Angular 21-ready) GitHub
- ngbootstrap Datagrid inline editing docs ngbootstrap
- ngbootstrap Drag & Drop Form Builder demo ngbootstrap
- Export to PDF/Excel docs ngbootstrap
