With the Angular CLI, when do you run `ng update <package> --migrate-only --from <version>`, and how does it differ from a normal `ng update`?
answer
- versions already bumped elsewhere
- no install, migrations only
- --to defaults to the installed version
- --name runs one migration
- one package at a time
basics
~20 sUse it when versions were bumped outside ng update (a package manager, a bot, a shared catalog), so migrations never ran. It installs nothing and runs migrations above --from up to --to, which defaults to the installed version.
solid answer
~50 sA normal `ng update` resolves new versions, installs them and then runs migrations for the range between old and new. `--migrate-only` skips resolving and installing: it takes the package as it is installed now and runs its migrations. `--from` says where the code actually is, and `--to` says how far to go, defaulting to the installed version; `--to` requires `--from`. It is for when versions were changed without `ng update`: a dependency bot's pull request, a manual install, a failed update you finished by hand, or pnpm/yarn catalogs, which the CLI cannot edit and for which it prints the exact `--migrate-only --from` commands to run. `--name <migration>` instead runs one migration by name, such as an optional one you skipped. These options work only with a single package, and `--name` cannot be combined with `--from`/`--to`.
code
bash · 6 lines# versions were bumped by a bot from 21.2.3 to 22.2.0
ng update @angular/core --migrate-only --from 21.2.3
ng update @angular/cli --migrate-only --from 21.2.3
# run a single, previously skipped optional migration
ng update @angular/cli --name use-application-buildergo deeper
Know that migrations only run through ng update, and that --migrate-only exists to run them later.
Use --migrate-only with --from and --to, and --name for a single migration, and know the single-package restriction.
Recover repositories where versions moved without migrations (bots, catalogs, interrupted updates) and choose the correct from-version from history.
Set up dependency automation so Angular majors go through ng update, or through a documented migrate-only step, rather than silent version bumps.
## Why migrations can go missing Migrations run as part of `ng update` after the install, for the version range between what was installed and what was just installed. Any other way of changing versions skips that step: - a dependency bot or a teammate bumps `@angular/core` in `package.json` and runs an install; - a monorepo defines versions in a **pnpm or yarn catalog**, which `ng update` refuses to edit; - an `ng update` was interrupted after the install, or someone reverted the migrated code by mistake. The versions are now new while the code is still old. `--migrate-only` exists to catch up. ## What the options do | Option | Meaning | |---|---| | `--migrate-only` | Do not resolve or install versions; only run migrations for the named package | | `--from <version>` | The version the code was last migrated for; migrations above it run. Implies `--migrate-only` | | `--to <version>` | The upper bound; requires `--from`. Defaults to the installed version | | `--name <migration>` | Run a single migration by name. Implies `--migrate-only`; cannot be combined with `--from`/`--to` | All of them are only available when **one package** is named. A version specifier on the package (`@angular/core@22`) has no effect in this mode, and the CLI warns about it. The selection rule is the same as for a normal update: a migration runs when its `version` is greater than `--from` and no greater than `--to`. Required migrations run in version order, and optional ones are offered in a prompt. ## Typical uses 1. **After a bot bump.** The pull request moved `@angular/core` from 21.2.3 to 22.2.0 without migrations: ```bash ng update @angular/core --migrate-only --from 21.2.3 ``` 2. **Catalog-managed monorepos.** When target versions come from a catalog, `ng update` stops, lists the packages and prints the steps: update the catalog file by hand, run the install, then run `ng update <pkg> --migrate-only --from <current>` for each package. 3. **A skipped optional migration.** Optional migrations are listed during the update, and in a non-interactive terminal each is printed with `ng update <pkg> --name <migration>`, for example: ```bash ng update @angular/cli --name use-application-builder ``` 4. **Replaying a range.** `--from 21.0.0 --to 21.2.0` re-runs only that slice, for example after rebasing a long-lived branch onto a migrated main. ## How it differs from a normal update - **No version resolution or install.** The peer dependency check and the one-major rule, both parts of version resolution, are not involved. You are responsible for the versions already being right. - **One package at a time.** A normal update can name several packages and follows package groups. Here you run it per package: `@angular/core` for framework migrations, `@angular/cli` for workspace migrations, and each library separately. - **Same safety rules.** The clean-tree check and `--create-commits` apply as usual. ## Checking what ran The log has the same shape as a normal update. `** Executing migrations of package '@angular/core' **` starts the package. Each migration prints its title and description, then `Migration completed (N files modified).` or `No changes made`. Optional migrations are listed separately with a prompt. Two habits make the run auditable: 1. Add `--create-commits`, so each migration that changed files becomes its own commit named after it. 2. Build and run the tests straight after, exactly as after a normal update, because the code is only now catching up with the installed versions. If a migration fails, the run stops with `Migration failed. See above for further details.` Migrations work on the schematics virtual tree, so the failed one leaves no half-written files. Fix the cause and re-run. Migrations earlier in the range run again, which is usually harmless because most are written to be idempotent, and per-migration commits show exactly what changed the second time. ## Pitfalls - **Wrong `--from`.** Too low re-runs migrations that already ran; most are written to be idempotent, but not all are harmless. Too high skips needed ones. Take the value from git history of `package.json` or the lockfile. - **Forgetting the CLI package.** Bumping `@angular/cli` outside `ng update` also skips `@schematics/angular` workspace migrations, which `ng update @angular/cli --migrate-only --from ...` runs. - **Using it to jump majors.** It runs migrations, but it does not make an install across several majors supported.
- Why can't you combine `--name` with `--from`?They select migrations in different ways. `--name` picks one migration explicitly, whatever its version. `--from`/`--to` select every migration in a version range. The CLI declares them as conflicting options, so you choose either a named migration or a range.
- How do you find the right `--from` value after a bot bump?Look at the version before the bump: the previous `package.json` or lockfile entry in git history, or the bot's pull request title. Use the version the code was last migrated for. Everything above it, up to the installed version, then runs.
saying these in an interview costs you the question
- Thinks --migrate-only also installs the requested version
- Passes several packages to --migrate-only in one command
- Believes bumping versions with a package manager runs Angular migrations
- Combines --name with --from and expects both to apply
- Guesses --from instead of taking it from git history