Improve the first contentful paint time in Angular using the app shell technique



The angular app shell is a special technique to show very minimalist content until the web browser downloads the actual content of the page, So how this matter? This technique helps in two ways, firstly it improves the customer experience by letting users know that the web page is loading, and secondly, and the most important thing it reduces First Contentful Paint time drastically, and hence it improves your page performance score.

There are many questions asked around Angular App shell technique in the official Git Repo, So in order to help the community, Git repository has been added for reference and hosted these applications in Heroku so that anyone can easily compare the results in PageSpeed Insights.

Angular WITHOUT app shell

  1. Github Repo:https://github.com/allabouttech0803/universal-tutorial-without-app-shell
  1. Demo URL:https://universal-tutorial-woapp-shell.herokuapp.com/tutorials

Angular WITH app shell

  1. Github Repo:https://github.com/allabouttech0803/universal-tutorial-with-app-shell
  1. Demo URL:https://universal-tutorial-app-shell.herokuapp.com/tutorials

After performing multiple tests, indeed the application with App shell enabled wins by 9% on average compared to the application without app shell and please note that performance is analyzed from web.dev

Performance results of Angular Application WITHOUT App Shell



Performance results of Angular Application WITH App Shell



As you can notice from the above results that the first contentful paint time is reduced by 1.2 seconds this will help in showing the users as early that the web application is loading

So now if you want to enable the app shell to your existing application follow these steps to add app shell to your existing application.

Step 1): Generate the the shell component by executing below command

ng generate app-shell

Step 2): You can notice that there are some changes in your workspace after running the above angular schematics, such as there is a new entry in angular.json for app-shell and also majorly there is new file introduced named with app.server.module.ts as shown below, In fact, this is where all magic happens as you can see that there is new route is introduced with 'shell' and this will be pre-rendered during build time itself and displayed to the users until real content is loaded.

Note: Currently there is a bug in Angular schematics generation, in case if you are using default route with a path ** in app.routing.module.ts then the app shell doesn't load hence as a temporary fix we have to reset the route configuration in the constructor of app.server.module.ts hence we manually added the line this.router.resetConfig(routes);

ng run projectname:app-shell --configuration=production

Step 3): Then add the necessary view, you want to display in the app shell component, and here major point to be noted that you should add very minimal HTML, CSS content so that the browser loads the app shell very quickly. The best option for this would be to add the spinner view in your app shell component so that it’s an indication to the users that the application is still loading. you can see we have added the HTML and CSS to view the app shell as shown below,



Step 4): Then the most important thing is you need to build your application using the below command, here you replace projectnamethe string with your actual project name present in the angular.json

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { Routes, RouterModule, Router } from '@angular/router';
import { AppShellComponent } from './app-shell/app-shell.component';

const routes: Routes = [{ path: 'shell', component: AppShellComponent }];

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    RouterModule.forRoot(routes),
  ],
  bootstrap: [AppComponent],
  declarations: [AppShellComponent],
})
export class AppServerModule {
  constructor(private router: Router) {
    this.router.resetConfig(routes);
  }
}

References

  1. https://angular.io/guide/app-shell
  2. https://blog.angular-university.io/angular-app-shell
  3. https://github.com/angular/angular-cli/issues/12310
  4. https://developers.google.com/web/fundamentals/architecture/app-shell
  5. https://github.com/angular/angular-cli/issues/8929
0
0