• instagram
  • twitter

SpringTree

logo
leaves
leaves
leaves

Maximize performance in Angular

Talking about Angular's performance and how to optimize and maximize it to get the most out of your project!

I’ve been using Angular ever since I joined SpringTree a few years ago. Since then I always stayed on top of all innovations inside the Angular ecosystem that improve performance. A few that come to mind are: relying more on RxJS, reactive programming patterns, onPush change detection and trackBy functions. In all this research I came across RxAngular, a “toolset for handling fully reactive Angular applications”.

Up until this point, my focus has been on using native Angular components to optimize performance. Today I would like to find out what an alternative library like RxAngular can offer. They recently released a 1.0 version of their @rx-angular/template library. Let’s take a look at what they have to offer.

What is RxAngular?

RxAngular is a set of libraries that help with developing high performance Angular applications. They contain a state management library, a template rendering library and a CDK. You can read more about RxAngular on their website. For this post I will be focusing on the template library.

Improving templates

The @rx-angular/template package contains alternatives for a set of common Angular pipes and directives, including the async pipe and ngFor directive. These include all kinds of optimisations and even allow you to run Angular without the dependency on ngZone. They call the latter: zoneless.

Change detection strategies

Angular has multiple strategies for change detection. The default, and the one most of you will be familiar with, is Zone.js. With this method, Angular will mark components that could have changed and Zone.js will check for changes in all these marked items. This check for changes will be performed in the whole component tree (see image). So not only the component that changed is checked, but all components in the whole tree. As you can imagine, this strategy is quite inefficient. Your application will be checked tens or hundreds of times every time a change is made. That’s why I recently started using a different strategy: on push change detection.

change detection.gif

With on push change detection Angular only marks items that could have changed if they meet certain criteria. These criteria, as far as I understand, are when one of the component inputs changes and when an observable - subscribed to in the template using the async pipe - changes. This limits your flexibility, but enforces good development practices towards reactive programming.

The last option is to (partially) disable reliance on Zone.js. In this mode, change detection only happens after you explicitly request this. While this is really performant, this is really cumbersome to implement and work with. The utilities in @rx-angular/template can give you the performance improvements, without the negative impact on DX.

What I’ve found

For testing I built a simple Pokedex using the PokeAPI. I’ll share my findings here.

The first thing I ran into is that RxAngular is not yet up to date with Angular 16. For now I just downgraded to version 15, but if you’re starting a new project this could be a dealbreaker.

RxLet

The rxLet directive is very handy! With this, you won’t need the “ngIf trick” to subscribe to an observable. See the difference below:

<ng-container *ngIf="number$ | async as number"> ... </ng-container>
<ng-container *rxLet="number$; let number"> ... </ng-container>

This enables some cool extra options, because you can use the value of number before it is set. This allows you to access the loading and error states.

<ng-container
 *rxLet="observableNumber$; let n; let s = suspense; let e = error, let c = complete"
>
 {{ s && 'No value arrived so far' }}

 <app-number [number]="n"></app-number>

 There is an error: {{ e ? e.message : 'No Error' }} Observable is completed:
 {{c ? 'Yes' : 'No'}}
</ng-container>

RxFor

I’ve found that RxFor is just an all around more optimized version of the standard ngFor. One neat trick is to specify the trackBy property inline. TrackBy functions are used to reduce re-rendering items in a for loop. You can do this by supplying a function that checks if items have changed. Usually this is just a simple function that checks the ID of an object. With RxAngular, you can specify the property you want to check inline.

<ul>
  <li *rxFor="let item of items; trackBy: 'id'">{{ item }}</li>
</ul>

This saves you from creating a function and makes it immediately clear what the trackBy function does.

RxIf

The same as RxFor is true for RxIf, with the main benefit being that you can pass observables directly.

<app-item *rxIf="show$"><app-item></app-item></app-item>

Other libraries

The state management library is cool too. They implemented all kinds of optimisations on top of observables. So, if you’re heavily leaning on RxJS already for managing state, do take a look. A great introduction video can be found here.

The SDK contains helpful utilities to take your maximum performance journey to the next level. Although, personally I haven’t tried these.

Signals

I really enjoy investigating how to improve an app’s performance and I think RxAngular is a must have if you’re starting a new Angular project. Hopefully they sort out compatibility with Angular 16 soon. The only question remaining is: what about signals?

Angular introduced signals in the latest version, and they will be fully featured in version 17. When used exclusively, you won’t need observables anymore (at least not in the template) and you can even go fully zoneless using signals. Some exciting changes are happening, and I feel like the Angular space is finally moving again. If signals deliver what they promise, we might not even need libraries like RxAngular anymore. Perhaps only a good state management solution in combination with signals is enough to get maximal performance in Angular. The future is exciting!

Conclusion

At SpringTree, we constantly try to use the latest and greatest technologies, but won’t shy away from building on top of existing ones. As a Svelte fanboy I would advise to use SvelteKit on new projects, but for existing ones this is not always feasible. We can use tools like RxAngular to cost effectively optimize existing applications and deliver the best user experience we can.