Using App Shell to Improve Performance – Angular 6

Angular apps take time to show meaningful content to the user. This time is mainly after loading the index.html page and bootstrapping the app. This is especially worse on lower end devices or slower networks, where it takes longer to get the all files required to bootstrap the app.

It is therefore important to show some animation or an app shell, to indicate to the user that your web app is not broken. When it takes longer to paint something on the web page, most users will likely leave your app, most likely for a competitor. And we all want to avoid that.

There are several ways around this, the first one, is using a simple loading animation, indicating to the user something is happening on the background. And the second option, is loading the app shell – title bar, navigation etc. of the app, then some animation where the data will be placed after bootstrapping.

Let’s first look at the loading animation, and then the app shell option:

Using a Simple Loading Animation/Message

This is the easiest option, it involves using CSS and html to create an animation or print a message to the user, as they wait for the app to load and bootstrap. This is important, as it indicates to them, that something is happening in the background instead of a blank page.

Achieving this is rather straightforward, open the index.html file, under the src directory in the root of your angular workspace. Add the animation code in between  <app-root> </app-root> tags. Why here? This is because, after the app has successfully bootstrapped, it will replace the content in between with the application itself. So, our animation code will look something like this:

<app-root>
  <div class="text-center bg-primary p-2" style="height: 100vh;">
    <div class="container align-middle p-5">
      <i class="fa fa-spinner fa-spin fa-3x"></i><br>Loading ...
    </div>
  </div>
</app-root>

I am using font-awesome and bootstrap for this demo, so remember to add the minified CSS imports at the header section.

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">

And this is what it looks like:

Using App Shell to Improve Performance – Angular 6

NB: You  can make it lighter and easier to load by using pure CSS to load the animation, instead of using Bootstrap and Font Awesome, which is overkill for a simple animation. But if you are using both of these libraries somewhere else, this is a great way to load them and keep them outside your final angular bundle.

Using an App Shell

Now, let’s take the above process a step further. We can use angular cli to generate an app shell for a specific route. An app shell is the skeleton of an app – navigation, title bar, menu etc. In angular, you can prerender a specific route, to create an app shell which will be show to the user as they wait for the to bootstrap.

This will include all components, in between the app-root and the router outlet of the prerendered route. The component to be loaded in the router-outlet is then replaced by the app shell component, this component is where you would add a loading animation or message.

NB: Unfortunately, as far as I can tell from my own research, you can only create for one route in an angular project.

So, let’s first generate an app shell for our application, using the ng generate app-shell command:

$  ng generate app-shell --client-project angular-app-shell-demo --universal-project angular-app-shell-demo-rendered

Where: client-project is the project you want to generate the app shell for. And —universal-project is the name of server app or universal app, the option we will use to prerender our app shell. You may also want to include a —route, if you don’t want to create an app shell for the default route.

Changes to your Angular Project

When you run the above command, it makes some changes to your angular application. If you look at angular.json, you will see a few modifications:

...
"server": {
  "builder": "@angular-devkit/build-angular:server",
    "options": {
      "outputPath": "dist/angular-app-shell-demo-server",
      "main": "src/main.server.ts",
      "tsConfig": "src/tsconfig.server.json"
      }
    },
"app-shell": {
   "builder": "@angular-devkit/build-angular:app-shell",
   "options": {
     "browserTarget": "angular-app-shell-demo:build",
     "serverTarget": "angular-app-shell-demo:server",
     "route": "shell"
   },
  "configurations": {
    "production": {
      "browserTarget": "angular-app-shell-demo:build:production"
    }
  }
}
...

Another key change to take note of, is the component generated – AppShellComponent. This is a normal component, apart from the fact that it is registered with Server Module instead of the App Module. The Server Module was also generated by the generate app-shell command and can be found in the same directory as the app module.

Building your Angular Application

To build our application with an app shell, all you have to do is run the following command:

$ ng run angular-app-shell-demo:<app-shell-name>

Replace the <app name> with the name of the app shell name, you specified when generating the app shell. This will first prerender the path, and then build your angular app just as normal. But, if you look at your final index.html, you will notice it has extra content, unlike a normal one:

<app-root _nghost-sc0="" ng-version="6.1.9">
  <div _ngcontent-sc0="" class="container">
    <nav _ngcontent-sc0="" class="navbar navbar-light bg-light"><a _ngcontent-sc0="" class="navbar-brand" href="#"><img
            _ngcontent-sc0="" alt="" class="d-inline-block align-top" height="30" src="/docs/4.1/assets/brand/bootstrap-solid.svg"
            width="30"> My Application </a></nav>
    <router-outlet _ngcontent-sc0=""></router-outlet>
    <app-app-shell _nghost-sc1="">
      <div _ngcontent-sc1="" class="row">
        <div _ngcontent-sc1="" class="col-12 text-center"><i _ngcontent-sc1="" class="fas fa-spinner fa-spin"></i>
          Loading application </div>
      </div>
    </app-app-shell>
  </div>
</app-root>

All the above content is pulled from all components content right up to the router-outlet of the path. So, any modification you make to the components, will be reflected to the app shell when you build the application. This is certainly better than the first option of adding a loading animator directly to the index.html.

To view the app shell, all you have to do is navigate to the route you generated for the app shell. In our case it was the default route, so navigating to it, you will see loading message from the app shell component, before being replaced by the actual content from the message.

Using App Shell to Improve Performance – Angular 6

Source Code

You can find the source code of the app shell here.

2 Replies to “Using App Shell to Improve Performance – Angular 6”

  1. Thanks for the article, I did not knew that we have such great feature along with many other features of Angular Universal.
    I am getting an error saying
    “navigator is not defined
    ReferenceError: navigator is not defined
    at Object../src/main.server.ts”

    1. Hi Samiullah Khan, can you share the code for main.server.ts, most likely a module was not installed successfully or there is a conflict with Angular Universal. I can’t tell for sure unless I see your code. You can share privately if need be.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.