Angular advance tips & best practices for experts and beginners

I wrote this article and included some best practices, pitfalls, info, and ‘ what to look out’ when building an angular application that could be useful for angular devs. A lot of these had been personal experience and lessons learned the hard way. Also, it was really difficult to create this list, keep it short and simple since I wanted to add stuff for beginners and experts.

Try to follow what angular recommends and not do things your way

Since you’re using Angular framework you should follow what the creator suggests and not do things your way. This goes for most opinionated frameworks not just angular.

A MUST DO starting point is to read and follow the angular styleguide

If you’re new to angular start from tour of heroes

Its one of the best tutorials with very good documentation out there that helped me a lot back when I started with angular

Read the Documentation

The documentation is mature and almost complete. Besides that, you should check and be comfortable with angular Glossary

Also, other things to check.

Consider using a UI Component library

you can choose between angular material, primeng and many others that will make your life much easier

Use when upgrading version

Need to upgrade from older version to a newer and don’t know what will break, or what to change? No worries. Visit choose the version you currently have, choose the version you want to go, and follow the instructions. It also includes a guide for angular material.


Search for Angular meetups near your area

Do you know angular has its own Blog?

Top Angular Events

Also, don’t hesitate to open/report an issue when finding a problem regarding angular itself or third party will only make things better

Versioning / package.json, remove all caret ‘^’ and tilde ‘~’

If you want your app/library to be stable and not fail after a few months, you should highly consider removing all caret, tilde symbols from package.json.

Do not blindly accept automatic dependencies. You don’t want people knocking on your door when things go bad because of auto dependency update.

The web is evolving dramatically year after year. Try to run npm install on a 3 to 6 month old project and you get a warning plus deprecation messages not to mention there is a chance for a build failure.

If you want to have more control of your versions you can set this

npm config set save-prefix=''

Invest time staying up to date

Its recommended to invest some time once every 3 to 6 months and keep up to date your dependencies. Remember a little warning or deprecation notice today will become 10 or 20 notices in the future and will bring higher problems.

If you are in the position you running angular 4, the latest version is 9 and you need to upgrade. Well bad news for you, it’s going to be a tough unpleasant job. Because it’s not just the Angular you have to upgrade, it’s also all the dependencies that rely on.

Remove unused imports and dependencies

Use types instead of Any

Avoid using any and prefer types. Use any if there is no other option or for special cases. That’s why you’re using angular and typescript in the first place.

Know your package-lock.json

Remember your package-lock.json is the source of truth

package-lock.json lists the correct dependency versions your app is using. Refer to package-lock to know the exact version of a dependency your using.

For example, you may have this in your package.json ‘^1.1.1’ but in runtime, you are using this ‘1.2.2’

run npm ls module-name to get the exact module version.

package-lock is generated automatically from any operation that modifies package.json or node_modules tree, assuming you have node > 5.x.x

Also, run npm ci for clean install from package-lock

Watch out for dev, prod differences

Don’t take for granted that everything will be working just like development with prod build.

For example in development mode change detection runs twice. The second time it runs( after the first completes ) if the bound values are different it produces the famous error (ExpressionChangedAfterItHasBeenCheckedError )

Build-optimizer and vendor-chunk

Setting vendorChunk to true in prod build the libraries will be placed in a separate vendor chunk.

remember, if buildOptimizer is true, set vendorChunk to false. using a build optimizer includes also the library code in the bundle, resulting in a smaller size.

Use vendor chunk when:

  • There is a lot of client updates without affecting much of the third-party dependencies/libraries

Do not use vendor chunk when:

  • The bundle size difference is significantly larger
  • There are no frequent client updates

Service in subModule Providers

Services are singleton in the module provided. Adding them in other module providers will create a new instance

Consider using in Injectable providedIn root | any | platform

prior to angular 6 and later we can use providedIn:'root' in Injectable instead of importing the service in a module.

Also, angular 9 introduced us with extra providedIn options, any and platform

By declaring a providedIn in Injectable service we don’t have to import in any module. Instead, if we add the service in providers, we also have to import it, which will be included in the final bundle even if we don’t use it anywhere.

By using providedIn which is the official preferred way it enables Tree Shaking. With Tree Shaking, if the service isn’t used anywhere it won’t be bundled with the rest of the code which leads to smaller bundle size and faster loading times

What about providedIn root | any | platform options?

The time of writing this article, the official angular documentation of providedIn only covers root. So I will try to explain any and platform as simple as possible.

  • root: One instance of the service for the whole application
  • any: One instance for the eagerly loaded modules, different instance for every lazy module
  • platform: One instance for all angular applications running on the same page (useful for shared service modules)

Lazy loading

Please lazy load your modules and don’t eager load them all at once. It makes a tremendous difference in loading time even for small applications. Consider eager loading for core modules and feature modules that required to start the app and do some initial interception.

I’ve seen big applications with all modules been eager load, and it some cases it exceeds 30 seconds to start.

You can check angular lazy-loading doc

Lazy loading loadChildren from a secure place

If the resources and time are available you may consider store loadChildren path in a secure place and fetch them after authorization.

Choose angular i18n (even if it’s not mature yet) instead of ngx-translate

Yes, I know, you probably think that angular implementation of i18n sucks!. You have to create different builds for every language and to reload for every language change.

And you are right! It was a living hell for me and my team when working with these.

But…. keep in mind, angular i18n is going through a lot of development effort and it is very complex. Angular is pushing it and eventually will become a lot better. Also, the main developer of ngx-translate is hired by angular and working in i18n (source here). His statement is that ngx-translate is just a replacement for angular until angular i18n completes and at some point ngx-translate will be deprecated.

Use trackBy when you can

You rendered a collection with *ngFor and now you have to get new data from an HTTP request and reassign them to the same collection. This will cause angular removing all DOM elements associated with the data and re-render all DOM elements again. Having a large collection could cause a performance problem since DOM manipulations are expensive.

trackBy comes to the rescue. By using trackBy, rendering will happen only for the collection elements that have been modified(also new/removed).

Consider the following

export class AppComponent {
  //initial array
   cars = [
  setNewCars() { = [

    return index;
   <li *ngFor="let car of cars">{{}}</li>
<button (click)="setNewCars()">update cars</button>

By pressing the button, the whole ul HTML block will be re-render.

Let’s add trackBy by including the trackByFn and update the *ngFor

   <li *ngFor="let car of cars; trackBy:trackByFn">{{}}</li>

Now by pressing the button, the elements with a difference will be re-render. Which in our case are the updated item with id:2 and the new item with id:4

Angular webworker has been marked as deprecated and may be removed at angular 10

Change detection strategy: OnPush

By default, angular runs change detection on all components for every event that occurred. It’s the default ChangeDetectionStrategy.Default. Having many components and a large number of elements could result in performance issues. To deal with that we can set ChangeDetectionStrategy.OnPush on the components we want. For example.

export class ChildComponent {    

By settings ChangeDetectionStrategy.OnPush the ChildComponent it only depends on the input values and it will only run checks if the input values change.

If the input is a value type it will run a check for the new value.

For a reference type value, a check will run only if is a difference between the old and new input reference.

Keep note to a case when we have a different input reference and change detection is not running.

export class ChildComponent {    

        //Async get new data from http request
   = d;
            //No UI update at this point  

Having a similar code with above you notice even if the input reference is changing the component is not updating. That’s because there is not an event trigger to run the change detection in the first place.

The solution is to use inject ChangeDetectorRef and programmatically trigger change detection

export class ChildComponent {    
    constructor(private cd : ChangeDetectorRef){}
        //Async get new data from http request
   = d

Be careful with spamming events

As said above, every event triggers change detection. Be careful with global and window events that may cause unintended checks running on the whole app all the time

The notorious ExpressionChangedAfterItHasBeenCheckedError error

If you see this error it means there is something wrong with your code.

In development mode, angular runs two digests circles. The first time it evaluates the values and updates the DOM. If angular running on development mode the circle runs a second time. In the second circle, angular performs verification and compares the values against the first circle. If a difference is found it produces the error. That’s why this error means you have something very seriously wrong with your code. If the error wasn’t throwing two things could happen. First, for the values to be the same between oldValues and newValues it could lead to an infinite circle loop. And second, it could lead to an inconsistent model – view state.

A simple scenario that may cause the error

AppComponent HTML
<childComponent [data]="originalData"</childComponent>

  this.appComponent.originalData = "updatedValueData"

Tip: avoid manipulating a bound array directly in loop.

ViewChild/ContentChild availability

Both ViewChild and ContentChild are available from ngAfterViewInit and after. ngAfterViewInit is when the component view has been initialized.

ViewChildren and ContentChildren will return a QueryList while ViewChild and ContentChild will return the first element they matched.

Pure and impure Pipes

Pupe pipe is when angular executes the pipe only when input reference changes. By default pipes are pure. Consider the following pure pipe.

  name: 'jdmCars' 
export class JdmCarsPipe implements PipeTransform {
  transform(allCars: Car[]) {
    return allCars.filter(car => car.isJdm);

Now if we add a new car somewhere in our component code…


Nothing happens. The view is not updating. This is because the car array reference is the same. If we want to see the changes we have to make our pipe impure or re-reference(not recommended) the car array.

  name: 'jdmCars',
  pure: false

Base URL and Paths in tsconfig.json

Instead of heaving imports like this…

import { MyService }   from '../../services/MyService';
import { MyComponent } from '../../../../app/core/MyComponent';

Define the base paths you want in tsconfig.json

    "paths": {
      "@app/*": ["src/core/*"],
      "@assets/*": ["src/assets/*"],
      "@service/*": ["src/services/*"]
//And use them
import { MyService }   from '@service/MyService';
import { MyComponent } from '@app/MyComponent';

Will save you a lot of time in refactoring

Why not check out Redux

Depending on the situation and if state management of type undo/redo is needed, you could consider using redux for your next angular app. You could check out rxjs for angular.