← All articles

Angular 20 Update Impressions (Niche Edition)

Personal notes on Angular 20: control-flow migration, the move from @angular-devkit/build-angular to @angular/build, flattened library .d.ts output, and experimental API renames.

Published
Angular 20 Update Impressions (Niche Edition) cover image

Angular 20 is out. Nothing felt like a single dramatic breakβ€”development continues as before. For specifics, see:

https://blog.angular.dev/announcing-angular-v20-b5c9c06cf301https://blog.angular.dev/announcing-angular-v20-b5c9c06cf301
Watch this video on YouTubehttps://www.youtube.com/watch?v=FcDamOe1qxA

I'll leave developer-facing and technical deep dives to those sources. Here I share topics I found interesting while updating.

control-flow-migration

Running ng update surfaced this optional migration. I always think: they call it "optional," but with no ignore option and unknown follow-on migrations, it does not feel very optional. πŸ˜„

** Optional migrations of package '@angular/core' **

This package has 1 optional migration that can be executed.
Optional migrations may be skipped and executed after the update process, if preferred.

 Select the migrations that you'd like to run (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed)
❯◯ [control-flow-migration] Converts the entire application to block control flow syntax.

After this, Angular 20+ projects effectively leave behind NgIf, NgFor, NgSwitch, and the old syntax. Those APIs are deprecated and will be removed in v22 about a year from now. Personally, watching a certain OSS project claim "backward compatibility" while APIs pile up into debt makes me appreciate Angular's cycle:

  • Ship new replacement APIs
  • Hear from the community
  • Remove old APIs from user projects via automated migrations
  • Mark deprecated
  • Delete from the framework a year later and pay down debt

I love that turnover. It sits opposite the "it's mature so it's fine" crowd, but following along improves your projectβ€”that feels like a real framework.

Builder moves from @angular-devkit/build-angular to @angular/build

Tracing back, the first @angular/build commit is here:

https://github.com/angular/angular-cli/commit/810d213e1813dd01620173f5f999dca7bccf8ea1https://github.com/angular/angular-cli/commit/810d213e1813dd01620173f5f999dca7bccf8ea1

@angular-devkit/build-angular stays Webpack-based with backward compatibility; @angular/build is esbuild/Vite only. Looking at @angular-devkit/build-angular's package.json, @angular/build is in dependencies, so @angular-devkit/build-angular becomes the compatibility layer.

If you have kept migrating with Angular, v19 already defaulted to esbuild (@angular-devkit/build-angular:application), so this is mostly a non-issue. One edge case: if you configure the test runner in karma.config.js with @angular-devkit/build-angular/plugins/karma, that plugin does not exist on @angular/buildβ€”you still need @angular-devkit/build-angular alongside it.

Roughly: the builder became a lighter package without backward-compat baggage.

Library output no longer includes source-mirror .d.ts files

Through Angular 19, library output (dist) kept type definition files mirroring the project structure alongside built artifacts. Example output for my scroll-strategies library on Angular 19:

dist/scroll-strategies/
β”œβ”€β”€ fesm2022/
β”‚   β”œβ”€β”€ rdlabo-ngx-cdk-scroll-strategies.mjs
β”‚   └── rdlabo-ngx-cdk-scroll-strategies.mjs.map
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ dynamic-size-virtual-scroll-strategy.d.ts
β”‚   └── dynamic-size-virtual-scroll.service.d.ts
β”œβ”€β”€ index.d.ts
β”œβ”€β”€ package.json
β”œβ”€β”€ public-api.d.ts
└── README.md

The original project has src/lib/dynamic-size-virtual-scroll-strategy.ts and src/lib/dynamic-size-virtual-scroll.service.ts, so src contents flatten into lib/ as .d.ts files. Angular 20 changed that to:

dist/scroll-strategies/
β”œβ”€β”€ fesm2022/
β”‚   β”œβ”€β”€ rdlabo-ngx-cdk-scroll-strategies.mjs
β”‚   └── rdlabo-ngx-cdk-scroll-strategies.mjs.map
β”œβ”€β”€ index.d.ts
β”œβ”€β”€ package.json
└── README.md

If you never imported types from lib/dynamic-size-virtual-scroll-strategy, you may not notice (build settings matter too). Related issue: Flatten type definitions to a single *.d.ts file #139.

Libraries also moved from @angular-devkit/build-angular:ng-packagr to @angular/build:ng-packagr.

Experimental API changes

The Resources API had several breaking changes.
https://zenn.dev/rdlabo/articles/ff0bc7de29bd65https://zenn.dev/rdlabo/articles/ff0bc7de29bd65

Also, provideExperimentalZonelessChangeDetection was renamed to provideZonelessChangeDetection. Experimental APIs getting clearer names is welcome.

Summary

I have used Angular since Angular 2. Recently, from Signals through signal-based design and zoneless, the framework keeps getting easier to use.
Above all, I am not thrown by sudden breaking changesβ€”if I follow official upgrade guides, I naturally land on current patterns. It is not flashy, but steady evolution makes it a solid base for products.

See you next time.