20 Aug 2025
7 min

Angular 20.2 – the recent changes

Angular just got an update, and it comes with a bunch of new features worth checking out. Let’s break them down together.

Zoneless Angular is no longer in developer preview. It is stable now!

We’ve been waiting for this for a long time, and it’s finally here. In the latest version of Angular, Zoneless is now stable, so don’t hesitate to use it in your project. However, the focus of this article is to discuss the recent changes – the topic of Zoneless in Angular is quite extensive. If you want to learn how it works, click here.

Improved support for Typescript (5.9).
With the newest version of Angular, we can now take advantage of TypeScript 5.9 features, which are fully supported. This opens the door to several modern language improvements that can enhance both performance and developer experience.

For example, it’s now possible to import a module without triggering immediate execution:

import defer * as myFeature from "./common-module.js"
//no side effect//

//Here code is executed//
const value = myFeature.count();

It’s also worth mentioning that TypeScript 5.9 introduces a small but impactful improvement: the ability to see full interface types in editor tooltips and during hover inspection. This enhancement significantly improves the developer experience by making complex types easier to understand without jumping through multiple definitions. Take a look at the example below:

More information about the new version of Typescript can be found here.


Enhanced redirected property support.
First of all, let’s explain how this change can help us. Primarily it allows us to append our own logic regarding whether the request is redirected or not. See the following example:

export class RedirectServiceExample {
  private readonly _httpClient = inject(HttpClient);
  private readonly _userHandler = inject(UserHandler);
 
  getUser(Id: string) {
    return this._httpClient.get(`/api/users/${Id}`, { observe: 'response'     }).pipe(
      map((response: HttpResponse<any>) => {
        if (response.redirected) 
          return this._userHandler.processRedirectedUser(id)
        else return this._userHandler.processUser(response.body)
      })
    );
  }

We can see that our logic is appended conditionally, giving us full control over how requests are handled in this situation. This means that depending on whether a response was redirected, we can implement different handling strategies – for example, logging the redirect for analytics, applying additional security checks, or modifying the flow of the application

Secondly, it improves consistency with the native Fetch API. By exposing the redirected property, HttpClient now behaves more like the Fetch API, providing developers with a familiar and predictable interface.

Moreover, this new functionality allows us to better ensure the security of our applications. For a simple example: we are able to block unauthorized redirects or introduce additional verification when a redirect is detected in the application. This is certainly a useful feature in the context of cross-origin policies.

Analytics in the context of the redirected property is important because it allows you to track how often and under what circumstances the application is redirected. This helps assess the performance, stability, and compliance of the request flow with the intended architecture.

Need a quick way to catch up on Angular evolution? Our free ebook covers everything from Angular 14 to the latest version: features, use cases, and business impact. This is an all-in-one, practical guide you can reference anytime. Download it here.

New animation API. 

With the new animation API, we can avoid importing the animations module that has a very important impact on our application. First of all, it can reduce the final bundle size of our application. Nextly it is easier to handle animations using pure styles files rather then angular-specific libraries. Another change worth highlighting is the introduction of new functionalities: leave and enter, which allow us to animate our HTML elements without the need to import the animations module (as already mentioned above). With this approach, we can also make it easier for people who are just starting their journey with Angular to create animations. See how it works with the recent update:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent {
  private readonly _menuOpen = signal(false);
  protected readonly isMenuOpen = computed(() => this._menuOpen());


  protected toggleMenu() {
    this._menuOpen.update(value => !value);
  }


  protected closeMenu() {
    this._menuOpen.set(false);
  }
}

Html file:

<button (click)="toggleMenu()">Toggle Menu</button>


@if (isMenuOpen()) {
  <div
    animate.enter="animate-in"
    animate.leave="animate-out"
    (click)="closeMenu()"
  >
    <!-- Your content -->
    <p>Click to close</p>
  </div>
}

And finally part of our styles:

@keyframes animate-in {
  from {
    transform: translateY(50%);
  }
  to {
    transform: translateY(0);
  }
}


.animate-in {
  animation: animate-in 0.35s ease-in-out;
}


@keyframes animate-out {
  from {
    transform: translateY(0);
  }
  to {
    transform: translateY(50%);
  }
}


.animate-out {
  animation: animate-out 0.35s ease-in-out;
}

In my opinion it is better when it comes down to developer experience. See this example with used animationModules:

<h2>Old Angular Animations</h2>
<button (click)="toggleMenu()">Toggle Menu</button>


@if (isMenuOpen()) {
  <div
    @menu
    (click)="closeMenu()"
  >
    <p>Click to close</p>
  </div>
}
@Component({
 /…/,
 animations: [
    trigger('menu', [
      transition(':enter', [
        style({ transform: 'translateY(120%)' }),
        animate('0.35s ease-in-out', style({ transform: 'translateY(0)' }))
      ]),
      transition(':leave', [
        animate('0.35s ease-in-out', style({ transform: 'translateY(120%)' }))
      ])
    ])
})
export class AppComponent {
  private readonly _menuOpen = signal(false);
  protected readonly isMenuOpen = computed(() => this._menuOpen());


  protected toggleMenu() {
    this._menuOpen.update(value => !value);
  }


  protected closeMenu() {
    this._menuOpen.set(false);
  }
}

Abstracting from the applied inline styles, we can clearly see that the new functionalities are more pleasant to use. Moreover, thanks to the latest changes, the performance may improve for applications running, for example, on mobile devices. As we know, some animations from the library are performed using the CPU instead of the GPU, which can negatively impact the application’s performance.

Increased possibility of @else if statements.

With the introduction of the new Angular version, we can use the key word as to extract the fields of an object we’re interested in and use them directly in our HTML template. This now also applies to the @else if condition. Take a look – it’s that simple:

@Component({
 //…//
 template: `
    @if(user(); as user) {
      <p>{{user.name}}</p>
    } @else if(admin(); as admin) {
      <p>Hi admin {{admin.nickName}} </p>
    }
 `,
})
export class App {
  //…//
})

In this very simple case, we can simply read the user’s type and, using type narrowing, conditionally display different content in the template.

Extended diagnostic for uninvoked function.
Before the release of Angular 20.2 there was no additional check when the function is wrapped into text interpolation. Previously such a warning mechanism was implemented for signals:

@Component({
 template: `
   {{text}}
 `,
})
export class App {
 protected readonly text = signal<string>('Hello angular 20.2');
}

In such a situation following warning is displayed:

[WARNING] NG8109: text is a function and should be invoked: text() [plugin angular-compiler]

Similarly, since Angular 20.2 error is displayed when function is not invoked:

@Component({
  //…//
  template: `
    {{text}}
 `,
})
export class App {
  protected text(): string {
   return ‘Hello Angular 20.2’;
  }
}

However, I hope you avoid invoking functions directly in templates. This is a well-known anti-pattern in Angular that can lead to unnecessary change detection cycles and degrade application performance. Instead, you should consider using getters or pre-computed properties in your component class. 

Replacing getCurrentNavigation method.
Since Angular 20.2 the reactivity of our app has slightly grown. Right now we can invoke currentNavigation() from our Router instance and compute it instead using the getCurrentNavigation method that returns a Navigation object or null. currentNavigation is a signal so we can derive its state. Please keep in mind that since the recent update, getCurrentNavigation method is deprecated

@Component({
  //…//
})
export class App {
  private readonly _router = inject(Router);
  protected readonly navigated = computed(() =>
    this._router.currentNavigation()
  );
}

Modified FormArray API. 

Since the release of Angular 20.2 FormArray API has been changed. Previously, if you want to push an array of controls to FormArray you were forced to iterate through array and for each iteration push it to Form Array:

export class App implements OnInit {
 protected formArray = new FormArray<FormControl<string | null>>([]);
 private readonly _controls = [
   new FormControl({ value: '', disabled: false }),
   new FormControl({ value: 'Name', disabled: false }),
 ];


 ngOnInit() {
   // before Angular 20.2 release
   this._controls.forEach((control) => {
     this.formArray.push(control);
   })
 }

Right now you are allowed to push to form array in this way:

 ngOnInit() {
   // after Angular 20.2 release
   this.formArray.push(this._controls);
 }

When you push to formArray using iteration then for each one event is triggered. With the new method, the event is triggered only once, when all controls are pushed, which improves angular performance. 

Exposed refferer and integrity for httpResource

With each new version of Angular (see the article on Angular 20.1), httpResource has been progressively enhanced, and in the latest version it was extended with the referrer and integrity options. It’s worth mentioning here why we need these capabilities and how we can use them. Thanks to the referrer option, we can either specify or hide the source page from which an external HTTP request is made. On the other hand, the integrity option allows us to verify whether a given resource has been tampered with –  which has a significant impact on the security of our applications. Below you can find a simple example.

@Component({
  //…//
})
export class App {
   protected readonly resource = httpResource(() => ({
   //rest of settings//
   referrer: 'no-referrer',
   integrity: 'your_sha_key',
 }));
};

Allowed binding to aria attribute.

Since the recent Angular version, we can bind ARIA attributes directly without using the attr. prefix. This change was made not only to simplify developers’ work but primarily to improve server-side rendering. See the example below:

@Component({
 //…//
 template: `
   <button [ariaLabel]=”label”>Click me</button> //New way
   <button [attr.aria.label]=”label”>Click me</button> //Old way
 `,
})
export class App {
 protected readonly label = `New aria property binding`;
}

Summary
In my opinion, you should consider upgrading the Angular version in your project to version 20.2. The recent changes have introduced a lot of updates that affect both the application’s performance and the developer experience, which is certainly worth noting. To see what has been changed in previous version of angular, I’d like to encourage you to read this article:

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.