| |

Angular 127 🅰️ Migrating Between Angular Versions

⏱️ Estimated read: 14 min | |

Angular releases a new major version every six months, and each major version carries breaking changes, deprecations, and new APIs. Migrating between versions is not a single event but a recurring discipline. The framework provides ng update to automate most of the mechanical work, and the Angular Update Guide provides a checklist of breaking changes for each version transition. The migration is designed to be incremental: one major version at a time, with each step verified before the next.

The reason version migration is manageable in Angular is the schematic system. When you run ng update, the CLI downloads the new packages and executes schematics that rewrite your source code to comply with new APIs or deprecations. These schematics handle the mechanical conversions: renaming methods, updating import paths, adjusting configuration files, and flagging patterns that need manual attention. The result is that most upgrades are a matter of running the command, resolving the flagged issues, and verifying the build.

This chapter covers the ng update command, the Angular Update Guide, the incremental migration path, the common breaking changes across recent versions, the patterns for handling third-party dependencies, and the workflow that makes upgrades safe and repeatable.

Key point: Run ng update @angular/cli @angular/core to update to the latest stable release. Angular supports upgrading one major version at a time; skipping versions is not supported. The Angular Update Guide provides version-specific instructions. Use ng update to check for available updates before running the actual upgrade. Always work from a clean git state and verify tests before and after.


Why incremental version migration matters

The breaking-change problem. Each major version of Angular includes breaking changes. Some are automatic, handled by schematics; others require manual code changes. Upgrading one major version at a time ensures that each version’s schematics have the opportunity to transform the code correctly before the next version’s changes are applied.

The dependency problem. Angular’s packages are versioned together: @angular/core, @angular/cli, @angular/common, and the rest must be on compatible versions. Upgrading one without the others creates peer dependency conflicts. The ng update command updates them together, and the incremental path ensures that each version’s dependency requirements are satisfied before moving on.

The third-party problem. Libraries like Angular Material, NgRx, and ng-bootstrap have their own release cycles. They may lag behind the Angular core by a few weeks or months. Migrating one Angular version at a time gives the ecosystem time to catch up, so the third-party dependencies are available when the core upgrade is ready.

The verification problem. A migration that jumps multiple versions concentrates risk into a single change. If something breaks, the cause is ambiguous. A migration that proceeds one version at a time isolates each change, so a failure can be attributed to the specific version that introduced it.

The Node.js and TypeScript problem. Each Angular version requires a minimum Node.js version and a compatible TypeScript version. Angular 18 requires Node.js 18.19 or later; Angular 22 requires Node.js 20 or newer and TypeScript 6. The incremental path ensures that the environment is updated in step with the framework.


a. The ng update command

The ng update command is the primary tool for version migration. Without arguments, it lists the updates that are available.

ng update

The output shows which packages have newer versions and which have migrations available. This is the check step: it tells you what can be updated without changing anything.

To update the Angular core and CLI to the latest stable release:

ng update @angular/cli @angular/core

The command updates both packages together, because they are tightly coupled. It downloads the new versions, runs the migration schematics, and updates package.json.

To update to a specific major version:

ng update @angular/cli@^21 @angular/core@^21

The caret (^) means “the latest patch version of major version 21.” This is the recommended form for a version-specific upgrade, because it takes the latest patch release, which includes fixes since the initial major release .

The --next flag updates to the next beta or pre-release version:

ng update @angular/cli@next @angular/core@next

This is used for testing pre-release versions, not for production upgrades.

Other useful flags include --allow-dirty (update even with uncommitted changes), --force (ignore peer dependency mismatches), and --create-commits (create a git commit for each migration step) .


b. The Angular Update Guide

The Angular Update Guide at update.angular.io is the companion to ng update. It provides version-specific instructions based on your current version and target version. The guide includes both basic and advanced update paths, matching the complexity of the application .

The guide lists the breaking changes for each version transition and the manual changes that the schematics do not handle. For example, migrating from Angular 15 to Angular 16 requires replacing TestBed.get() with TestBed.inject(), because TestBed.get() was removed .

The guide also specifies the minimum Node.js version and TypeScript version for each Angular release. Checking these before upgrading avoids environment-related build failures.


c. The incremental migration path

Angular supports upgrading one major version at a time. Skipping versions is not supported because the schematics for each version assume the codebase is on the previous version.

If you are on Angular 15 and want to reach Angular 17, you must first upgrade to Angular 16, then to Angular 17. The official release documentation states that ng update supports migration automation and recommends migrating one major version at a time .

# From 15 to 16
ng update @angular/cli@^16 @angular/core@^16

# From 16 to 17
ng update @angular/cli@^17 @angular/core@^17

Each step should be verified before proceeding. The build must pass, the tests must pass, and the application must run. If a step fails, the problem is isolated to that version transition, which makes it easier to diagnose.


d. Common breaking changes across recent versions

Each Angular major version introduces changes that require code updates. The following table lists representative changes from recent versions.

VersionChangeWhat to do
16TestBed.get() removedReplace with TestBed.inject()
16Strict template checking defaultResolve template type errors or disable temporarily
17New control flow syntaxMigrate *ngIf/*ngFor to @if/@for
18Zoneless change detection (experimental)Adopt signals for state, OnPush strategy
19Incremental hydrationConfigure hydration for SSR
20Stricter template parserRemove this. from template expressions
21Zoneless architecture improvementsAudit ChangeDetectorRef.detectChanges() and NgZone.run() usage
22Change detection defaults to OnPush-styleUse signals or async pipe for async state
22strictTemplates enabled by defaultResolve template typing issues

The schematics handle many of these automatically. The ng generate @angular/core:control-flow migration, for example, converts *ngIf and *ngFor to the new @if and @for syntax . Other changes, like the this. removal in Angular 20, require manual updates or are flagged by the build .


e. Handling third-party dependencies

After updating Angular core and CLI, the next step is updating third-party libraries. Many support ng update and provide their own schematics.

ng update rxjs
ng update @angular-eslint/schematics
ng update @angular/material

If a library does not support ng update, it must be updated manually. Check the library’s migration guide for breaking changes and update the version in package.json.

Some libraries lag behind the Angular release. If a critical dependency does not yet support the target Angular version, the upgrade must wait. This is one reason the incremental path is recommended: it gives the ecosystem time to catch up between major versions.

Libraries that are deprecated or in maintenance mode should be replaced. The Angular team deprecated Protractor in 2021; applications still using it should migrate to a modern testing framework . Similar replacements exist for moment.js (dayjs, date-fns), node-sass (Dart Sass), and tslint (ESLint).


f. The safe upgrade workflow

The workflow for a safe upgrade follows a consistent pattern.

1. Check the current state. Verify the Angular version, Node.js version, and TypeScript version. Run ng version and node -v .

ng version
node -v

2. Ensure a clean working state. Commit or stash all changes. The upgrade modifies many files, and a clean git state makes it easy to revert if something goes wrong .

git status
git add .
git commit -m "chore: prepare for angular upgrade"

3. Verify tests pass. A failing test before the upgrade complicates the diagnosis after. Establish a green baseline .

npm test

4. Check for available updates. Run ng update without arguments to see what is available and which packages have migrations .

ng update

5. Run the update. Update core and CLI together, then update third-party packages .

ng update @angular/cli @angular/core

6. Review the changes. The schematics modify source files. Review the diffs to understand what changed and whether any manual adjustments are needed.

7. Resolve flagged issues. The build and linter flag patterns that need manual attention. Resolve them one at a time.

8. Verify the build and tests. Run ng build and npm test. The build must pass, and the tests must pass .

ng build
npm test

9. Run the application. Start the development server and exercise the critical flows. Verify that the UI behaves as expected.

10. Commit and document. Commit the upgrade as a single change or a series of changes, and document any manual adjustments that were made.


Complete Example Session

# ============================================
# PART 1: CHECK CURRENT VERSIONS
# ============================================
ng version
node -v
# ============================================
# PART 2: ENSURE CLEAN STATE
# ============================================
git status
git add .
git commit -m "chore: prepare for angular upgrade"
# ============================================
# PART 3: VERIFY TESTS PASS
# ============================================
npm test
# ============================================
# PART 4: CHECK FOR AVAILABLE UPDATES
# ============================================
ng update
# Lists available updates and migrations
# ============================================
# PART 5: UPDATE CORE AND CLI
# ============================================
ng update @angular/cli @angular/core
# ============================================
# PART 6: UPDATE THIRD-PARTY PACKAGES
# ============================================
ng update rxjs
ng update @angular-eslint/schematics
ng update @angular/material
# ============================================
# PART 7: MIGRATE CONTROL FLOW SYNTAX
# ============================================
ng generate @angular/core:control-flow
# Converts *ngIf/*ngFor to @if/@for
# ============================================
# PART 8: FIX FLAGGED ISSUES
# ============================================
# Replace TestBed.get() with TestBed.inject()
# Remove 'this.' from template expressions
# Resolve strict template errors
# ============================================
# PART 9: VERIFY BUILD AND TESTS
# ============================================
ng build
npm test
# ============================================
# PART 10: COMMIT THE UPGRADE
# ============================================
git add .
git commit -m "chore: upgrade Angular to latest version"

These ten parts cover checking versions, ensuring a clean state, verifying tests, checking for updates, updating core and CLI, updating third-party packages, migrating control flow syntax, fixing flagged issues, verifying the build, and committing.


Quick Reference

Update Commands

CommandPurpose
ng updateList available updates
ng update @angular/cli @angular/coreUpdate to latest stable
ng update @angular/cli@^21 @angular/core@^21Update to major version 21
ng update rxjsUpdate a third-party package
ng generate @angular/core:control-flowMigrate control flow syntax

Options

OptionPurpose
--nextUse prerelease version
--forceIgnore peer dependency mismatches
--allow-dirtyUpdate with uncommitted changes
--create-commitsCreate a commit per migration
--migrate-onlyRun migration without updating version

Version Requirements

AngularNode.jsTypeScript
1818.19+5.4+
1918.19+5.5+
2020.19+5.8+
2120+5.9+
2220+6.0+

Common Breaking Changes

VersionChange
16TestBed.get() → TestBed.inject()
17New control flow syntax
18Zoneless change detection (experimental)
20this. removed from templates
22OnPush default, strictTemplates default

Migration Path

FromToCommand
1516ng update @angular/cli@^16 @angular/core@^16
1617ng update @angular/cli@^17 @angular/core@^17
1718ng update @angular/cli@^18 @angular/core@^18

Best Practices

✅ Do This:

# Check versions first
ng version
node -v

# Commit before upgrading
git add . && git commit -m "chore: prepare for upgrade"

# Verify tests pass
npm test

# Update one major version at a time
ng update @angular/cli@^21 @angular/core@^21

# Review the diffs
git diff

# Run the build and tests after upgrading
ng build && npm test

❌ Don’t Do This:

# Skip versions
ng update @angular/core@^20  # ❌ from 18 to 20 is not supported

# Upgrade with uncommitted changes
ng update @angular/core  # ❌ no safety net

# Ignore peer dependency warnings
ng update --force  # ❌ resolve the conflicts first

# Forget to update third-party packages
# They may be incompatible with the new Angular version  # ❌

Common Pitfalls

PitfallWhy It HappensFix
Peer dependency conflictThird-party library not updatedUpdate the library or use --force temporarily
Build fails after upgradeBreaking change not handledCheck the Update Guide for the version
Tests failTest utility API changedUpdate the test code
Node.js version too oldEnvironment not updatedUpdate Node.js to the required version
TypeScript version mismatchNot updated with AngularUpdate TypeScript to the compatible version
Control flow syntax not migratedManual migration not runRun ng generate @angular/core:control-flow
Strict template errorsNew checking enabledResolve errors or disable temporarily

Real-World Examples

1. Check Versions

ng version

2. Commit Before Upgrade

git add . && git commit -m "chore: prepare for angular upgrade"

3. Update to Latest Stable

ng update @angular/cli @angular/core

4. Update to Specific Major

ng update @angular/cli@^21 @angular/core@^21

5. Update Third-Party Packages

ng update @angular/material @angular/cdk

6. Migrate Control Flow

ng generate @angular/core:control-flow

7. Fix TestBed.get

// Before
const service = TestBed.get(MyService);
// After
const service = TestBed.inject(MyService);

8. Remove this from Templates

<!-- Before -->
{{ this.dropDown.isOpen }}
<!-- After -->
{{ dropDown.isOpen }}

9. Verify Build

ng build --configuration production

10. Commit Upgrade

git add . && git commit -m "chore: upgrade to Angular 22"

Visual

Incremental Migration Path

┌──────────────────────────────────────────────────────────────┐
│  Angular 15                                                  │
│       │                                                      │
│       ▼  ng update @angular/cli@^16 @angular/core@^16        │
│  Angular 16                                                  │
│       │                                                      │
│       ▼  ng update @angular/cli@^17 @angular/core@^17        │
│  Angular 17                                                  │
│       │                                                      │
│       ▼  ng update @angular/cli@^18 @angular/core@^18        │
│  Angular 18                                                  │
│       │                                                      │
│       ▼  ...                                                 │
│  Angular 22                                                  │
│                                                              │
│  One major version at a time. Verify each step.              │
└──────────────────────────────────────────────────────────────┘

ng update Workflow

┌──────────────────────────────────────────────────────────────┐
│  1. ng update (check)                                        │
│       │                                                      │
│       ▼                                                      │
│  2. Commit clean state                                       │
│       │                                                      │
│       ▼                                                      │
│  3. Verify tests pass                                        │
│       │                                                      │
│       ▼                                                      │
│  4. ng update @angular/cli @angular/core                     │
│       │                                                      │
│       ▼                                                      │
│  5. Schematics run automatically                             │
│       │                                                      │
│       ▼                                                      │
│  6. Resolve flagged issues                                   │
│       │                                                      │
│       ▼                                                      │
│  7. ng build && npm test                                     │
│       │                                                      │
│       ▼                                                      │
│  8. Commit                                                   │
└──────────────────────────────────────────────────────────────┘

Version Requirements

┌──────────────────────────────────────────────────────────────┐
│  Angular 18: Node.js 18.19+, TypeScript 5.4+                 │
│  Angular 19: Node.js 18.19+, TypeScript 5.5+                 │
│  Angular 20: Node.js 20.19+, TypeScript 5.8+                 │
│  Angular 21: Node.js 20+,    TypeScript 5.9+                 │
│  Angular 22: Node.js 20+,    TypeScript 6.0+                 │
└──────────────────────────────────────────────────────────────┘

Common Breaking Changes

┌──────────────────────────────────────────────────────────────┐
│  v16: TestBed.get() → TestBed.inject()                       │
│  v17: *ngIf/*ngFor → @if/@for                                │
│  v18: Zoneless change detection (experimental)               │
│  v20: 'this.' removed from templates                         │
│  v22: OnPush default, strictTemplates default                │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
Update commandng update @angular/cli @angular/core
Check commandng update (no arguments)
Version-specificng update @angular/cli@^21 @angular/core@^21
Migration pathOne major version at a time
Update Guideupdate.angular.io
Node.js requirementVaries by version
TypeScript requirementVaries by version
Test utility changeTestBed.get() → TestBed.inject()
Control flow migrationng generate @angular/core:control-flow
Safety netClean git state, passing tests

Key takeaways:

  • Angular supports upgrading one major version at a time. Skipping versions is not supported because the schematics for each version assume the codebase is on the previous version. The ng update command handles the mechanical changes through schematics .
  • Run ng update without arguments to check for available updates. The command lists which packages have newer versions and which have migrations available. This is the first step before any upgrade .
  • Update @angular/cli and @angular/core together. They are tightly coupled, and updating one without the other creates peer dependency conflicts. Third-party packages are updated after the core .
  • The Angular Update Guide provides version-specific instructions. The guide at update.angular.io lists the breaking changes for each version transition and the manual changes the schematics do not handle .
  • Each Angular version requires specific Node.js and TypeScript versions. Angular 18 requires Node.js 18.19+ and TypeScript 5.4+; Angular 22 requires Node.js 20+ and TypeScript 6.0+. Verify the environment before upgrading .
  • Common breaking changes are handled by schematics or flagged by the build. TestBed.get() became TestBed.inject() in Angular 16. The this. prefix was removed from templates in Angular 20. Strict template checking and OnPush defaults arrive in Angular 22 .
  • The safe workflow is consistent. Commit a clean state, verify tests pass, run ng update, resolve flagged issues, verify the build and tests, and commit the upgrade. The safety net is a clean git state and a passing test suite .

Remember: Migrating between Angular versions is a recurring discipline, not a one-time event. The ng update command automates the mechanical work through schematics, and the Angular Update Guide provides the checklist for the manual changes. The incremental path — one major version at a time — ensures that each version’s schematics have the opportunity to transform the code correctly and that the third-party ecosystem has time to catch up. The safe workflow is always the same: check, commit, verify, update, resolve, verify, commit. With a clean git state and a passing test suite, the upgrade is a controlled change rather than a risky leap. The Angular team designed the update tooling to make version migration predictable, and following the prescribed path keeps applications current without disrupting operations.


Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!