Here are a few hand-picked strategies to help you tune your Angular app for better performance. I hope you'll find them useful in your own work. Let's dive in.

Inlining Critical CSS

This is a relatively fresh and handy feature. At build time, Angular inspects your CSS selectors and checks them against the page that will be rendered. Based on that, it decides whether a style belongs to the critical rendering path and then embeds those styles directly into the HTML. Turning this on can positively affect your page's First Contentful Paint metric.

To activate critical CSS inlining, tweak the optimization browser builder option inside your angular.json file:

"optimization": { 
  "styles": {
    "minify": true,
    "inlineCritical": true // enable critical css inlining
  }
}

If you're using Angular Universal for SSR and want to take advantage of Critical CSS Inlining, make sure to enable it in the server.ts file as well:

  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
    inlineCriticalCss: true
  }));

Inlining Fonts

This is another technique for boosting First Contentful Paint. This optimization can be switched on when you're pulling in a Google Font; Angular will then embed the font directly into index.html. That spares the browser an extra network round-trip to fetch and render the font.

Font inlining is also controlled through the optimization browser builder option in angular.json:

"optimization": { 
  "fonts": {
    "inline": true,// enable font inlining
  }
}

For more details on both Critical CSS Inlining and Font Inlining, check out the official documentation: https://angular.io/guide/workspace-config#styles-optimization-options.


Adopting the OnPush Change Detection Strategy

This approach stops Angular from running change detection across the whole component subtree, which results in faster execution. You can set the changeDetection property in your Component's metadata to enable OnPush:

@Component({
    selector: 'my-company',
    templateUrl: './my-company.component.html',
    styleUrls: ['./my-company.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
})

Doing so disables automatic change detection for the component and its child directives, yet it still allows change detection to be triggered explicitly. With OnPush enabled, Angular will only re-check the component in these three situations:

  1. When the component's @output reference gets updated.
  2. When a DOM event happens inside the component or one of its child directives.
  3. When change detection is explicitly called.

Here are two thorough reads on OnPush change detection:

https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4

https://blog.angular-university.io/onpush-change-detection-how-it-works/


Server-Side Rendering with Angular Universal

In an earlier article, I leveraged Angular Universal to server-side render a sample application to enhance user experience and improve Core Web Vitals scores.

Out of the box, Angular applications are rendered on the client. That means the browser fetches all the application JavaScript, boots up the app, makes the necessary API calls to pull in data, and finally paints the page. Angular Universal, in contrast, runs the application on the server, generates a static HTML snapshot, and sends that to the client for immediate display. Once that's done, the browser loads the JavaScript and re-hydrates the app on the client side. Consequently, the page appears much faster, and users see the rendered content sooner.

You can find comprehensive instructions on setting up and using Angular Universal in the official guide: https://angular.io/guide/universal


Implementing Lazy Loading

Lazy Loading Modules

By default, the entire Angular codebase gets delivered to the client as a single bundle. As your application grows, so does that bundle. Fortunately, Angular supports lazy loading for parts of your application, which improves the initial load time and thereby the Largest Contentful Paint (LCP) metric. Angular handles lazy loading through routing; in other words, you can defer loading any part of your app that has its own routing setup.

For a deeper look at lazy loading Angular modules, refer to the official documentation: https://angular.io/guide/lazy-loading-ngmodules

Lazy Loading Components

Thanks to the new Ivy renderer, lazy loading individual Angular components is now possible — a genuinely valuable feature. That said, it demands some extra manual effort, especially when your component includes child directives or child components.

Here's an excellent write-up on how component lazy loading works: https://johnpapa.net/angular-9-lazy-loading-components/


Using Available Tools

Last but not least, there are several utilities you can leverage to measure your application's performance — which is the first step toward optimization. Many of these tools also offer suggestions on how to resolve potential problems. The most notable ones are:

  • Lighthouse: an open-source tool bundled with Chrome DevTools, designed to enhance web page quality.
  • PageSpeed Insights: an online service that generates a performance report for a page on both Desktop and Mobile, along with a broad set of recommendations for fixing existing issues.