Dec 20, 2025 · Updated Jul 15, 2026

Angular 20 to 21: Breaking Changes and How to Fix Them

Angular21UpgradeVitestZonelessSignals

Karma is dead, zone.js is opt-out, and HttpClient changed. Step-by-step guide to upgrade Angular 20 → 21 with real migration commands and zero guesswork.

If you thought Angular 20 was a big shift, welcome to Angular 21.

While version 20 was about stabilizing Signals, version 21 is about removing the old guard. The “Angular Way” has fundamentally changed: zone.js is optional, Karma is dead, and RxJS is slowly retreating to the edges.

This isn’t just an update; it’s a new ecosystem. Here is what is going to break, and how to fix it.

TL;DR — upgrade Angular 20 to 21 in 4 commands:

npm install -g @angular/cli@latest ng update @angular/cli@21 @angular/core@21 ng generate @angular/core:karma-to-vitest ng test

The three breaking changes most likely to hit you: Karma is replaced by Vitest as the default test runner, HttpClient is now provided in the root injector by default, and new apps exclude zone.js. Details and fixes for each below.

The “Stop Everything” Breaking Changes

Before you run ng update, be aware that your build will likely fail if you rely on these legacy patterns.

Breaking changeWhat breaksFix
Karma → Vitest is now defaultCustom karma.conf.js or Karma-specific plugins/reportersng generate @angular/core:karma-to-vitest
HttpClient auto-provided in root injectorTests that mock HttpClient as absent; interceptor order in mixed NgModule/Standalone appsRemove redundant provideHttpClient() calls unless passing options
zone.js excluded in new appsCopy-pasting v21 tutorial code into an existing v20 app mixes Zoneless and Zone.js assumptionsUnderstand Zoneless before mixing, or migrate explicitly with ng generate @angular/core:zoneless-migration

1. The Karma Extinction Event (How Do I Migrate from Karma to Vitest in Angular 21?)

The most immediate shock for many teams will be ng test. Angular 21 has officially swapped Karma for Vitest as the default test runner.

What breaks: If you have a custom karma.conf.js or rely on specific Karma plugins/reporters, your test suite is now legacy code.

The Fix:

  • New Projects: You get Vitest out of the box. It’s faster, cleaner, and uses Vite.
  • Existing Projects: You aren’t forced to switch immediately, but the writing is on the wall. The CLI will nag you.
  • Migration: Run the schematic ng generate @angular/core:karma-to-vitest to attempt an auto-migration. It’s remarkably good at converting standard configs, but custom Webpack hacks in your test setup will need manual rewriting for Vite.

2. HttpClient is “Just There”

Remember adding provideHttpClient() to your app.config.ts or importing HttpClientModule?

The Change: HttpClient is now injected by default in the root injector.

What breaks:

  • If you have tests that mock HttpClient by expecting it not to be there, they might fail.
  • If you rely on HttpClientModule for complex interceptor ordering in a mixed NgModule/Standalone app, you might see subtle behavior changes.

The Fix: Remove explicit provideHttpClient() calls unless you are passing configuration options (like withInterceptors or withFetch). It cleans up your config, but check your interceptor execution order.

3. zone.js is Gone (For New Apps)

New apps generated with ng new will exclude zone.js by default.

What breaks: Nothing for existing apps (yet). Your polyfils.ts will keep importing Zone.

The Warning: If you copy-paste code from a new v21 tutorial into your existing v20 app, it might assume Zoneless behavior (using ChangeDetectorRef less often, relying on Signals). If you mix the two paradigms without understanding them, you’ll get “changed after checked” errors or views that don’t update.

The New Toys: Features You’ll Actually Use

Once you fix the build, v21 offers some incredible DX improvements.

1. Signal Forms (Experimental but Stable)

This is the feature we’ve been waiting for. No more valueChanges.pipe(...) spaghetti.

import { form, field } from '@angular/forms/signals'; // Define a reactive form model const loginForm = form({ email: field('', [Validators.required, Validators.email]), password: field('', [Validators.required]) }); // Access values directly as signals! console.log(loginForm.value().email);

Why use it: It’s type-safe by default and doesn’t require RxJS mastery.

Status: Experimental. Use it for new features, but maybe don’t rewrite your checkout flow just yet.

2. Angular Aria (Developer Preview)

A new library of headless primitives for accessibility.

Instead of fighting with aria-expanded and role="button", you use directives that handle the a11y logic while you handle the CSS.

<!-- Handles keyboard nav, focus, and ARIA roles automatically --> <div ariaMenu> <button ariaMenuItem>Option 1</button> <button ariaMenuItem>Option 2</button> </div>

3. Regex in Templates

Small but mighty. You can finally use regex literals in templates, perfect for @if logic without creating a helper function.

@if (email() | match: /@company\.com$/) { <span class="badge">Employee</span> }

The Upgrade Checklist

Ready to jump? Follow this order to minimize pain.

  1. Backup: Commit everything. Seriously.

  2. Update the Global CLI: Updating Angular generally involves two parts: the global CLI and the local project dependencies. Ensure your global CLI is up to date first (you might need sudo or Administrator privileges).

    # Optional: Uninstall the old global version first to avoid conflicts npm uninstall -g @angular/cli # Verify the npm cache npm cache verify # Install the latest global CLI version npm install -g @angular/cli@latest
  3. Update Local Project: Now update your local project dependencies:

    ng update @angular/cli@21 @angular/core@21
  4. Run the Diagnostics: Angular 21 includes smarter diagnostics. Pay attention to warnings about ngClass (soft deprecated in favor of [class.my-class]) and standalone migration opportunities.

  5. Check Your Tests: Run ng test. If it explodes, decide:

    • Path A: Keep Karma (add @angular/build:karma manually if removed).
    • Path B: Migrate to Vitest (Recommended).
  6. Optional: Go Zoneless: If you’re feeling brave, run the experimental migration:

    ng generate @angular/core:zoneless-migration

[!NOTE] This is “Agentic” territory. See our MCP Guide for how to let AI handle this complex refactor.

Summary

Angular 21 is the “clean slate” release. It sheds the weight of the last decade (Zone, Karma, Modules) to compete with modern frameworks like Svelte and Solid.

The upgrade might be bumpy due to the testing changes, but the destination—a faster, simpler, signal-driven framework—is absolutely worth it.


Already on 21? The next step is here: Angular 21 to 22: Breaking Changes and How to Fix Them. Or see every migration path in the Angular Upgrade Guide.

Written by

Antonio Cárdenas

Google Developer Expert in Angular · Verified GDE profile · Google Developers profile

My technical writing reaches 100,000+ developers in English and Spanish. I've migrated production Angular apps from v8 to v22 — everything I publish is built from real project work, not documentation re-reads.

Frequently asked questions

FAQs

When was Angular 21 released?

Angular 21 was released in December 2025. It is a major release that replaces Karma with Vitest as the default test runner, makes zone.js opt-out for new projects, and introduces Signal Forms as an experimental feature.

Is Karma removed in Angular 21?

Yes. Angular 21 replaces Karma with Vitest as the default test runner for new projects. Existing projects can still use Karma by manually keeping @angular/build:karma, but migration to Vitest is recommended. Run ng generate @angular/core:karma-to-vitest to auto-migrate standard configurations.

How do I upgrade from Angular 20 to Angular 21?

First update your global CLI: npm install -g @angular/cli@latest. Then update your project: ng update @angular/cli@21 @angular/core@21. After that, run ng test to check for test runner issues, and optionally run ng generate @angular/core:karma-to-vitest to migrate from Karma to Vitest.

Is zone.js removed in Angular 21?

zone.js is not removed but is now excluded by default in new Angular 21 projects generated with ng new. Existing projects still use zone.js unless you explicitly run the zoneless migration: ng generate @angular/core:zoneless-migration.

What are the main breaking changes in Angular 21?

The three major breaking changes are: (1) Karma is replaced by Vitest as the default test runner; (2) HttpClient is now provided in the root injector by default, so explicit provideHttpClient() calls may be redundant; (3) New apps exclude zone.js by default. The ngClass and ngStyle directives are also soft-deprecated in favor of [class.x] and [style.x] bindings.

Keep reading

Building something with Angular?

I write about Angular architecture, Signals and the tooling around them — migration guides, real upgrade paths, and the catches most posts skip.

Browse all articlesAbout me

More from the blog