Boost your Angular App using AppShell
Here is the Tour of Heroes app, everyone has built this or similar app. A material toolbar is introduced on top of existing app.
Everything looks fine, app is loaded, there is no lag.
Let’s play with network tab and simulate Fast 3G.
The app which was loading in ~1 secs took 2.2 secs.
The main culprit is the connection, which is slowing down all downloads. Once the required files are downloaded, browser starts rendering the contents.
Browser rendering is not that simple, there is lot more happening before browser starts to render the content.
How browser rendering works — behind the scenes
by Ohans Emmanuel , LogRocket
In old days with servlet/JSP/ASP based apps, index.html was returned with complete renderable HTML elements along with other information such as data, CSS, JS. There were no client side frameworks for dynamic rendering; browsers were able to render the contents directly.
Now with SPA, the index.html contains only container and everything is rendered dynamically. We see blank page till the framework (Angular) kicks-in and loads required component.
AppShell can help in this case
Here is side-by-side view of same app without and with AppShell with simulated Fast 3G speed. The app with AppShell shows some contents around 1.2 secs as compared to previous ~2.2sec
What is AppShell?
An application shell (or app shell) architecture is one way to build a Progressive Web App that reliably and instantly loads on your users’ screens, similar to what you see in native applications. — Addy Osmani
Let’s keep PWA out of the definition.
App shell consists of skeleton of your app, it will render appropriate placeholder components. Those components may not contain data, but user will get idea that something will be rendered here. Rendering this skeleton helps us to improve perceived performance of the app.
In our sample app, App shell is the header with navigation links.
How do we create App shell with Angular?
Generate App shell
ng generate app-shell --client-project my-app --universal-project server-app
- my-app is the name of your app
- server-app is the name for your server (Universal) app.
Is the name of the server-side app module to be generated.
Following changes are made to the project
- Added AppShell component
- AppServerModule & Main Server file
- Angular.json has new configuration
The above command will add configuration to your angular.json
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/my-app-server",
"main": "src/main.server.ts",
"tsConfig": "tsconfig.server.json"
}
},
"app-shell": {
"builder": "@angular-devkit/build-angular:app-shell",
"options": {
"browserTarget": "my-app:build",
"serverTarget": "my-app:server",
"route": "shell"
}
}
Run the app
ng run my-app:app-shell
This will build the app and contents will be stored in dist folder. If you inspect dist/index.html you will notice app-root is not empty.
<app-root>app-shell works!</app-root>
The contents under app-root are important, now the index.html is served with these contents. These contents will be rendered till browser fetches & bootstraps Angular and in-turn Angular loads component.
Customizing App shell component
You can customize app shell component contents, by default it contents app-shell works!. You can modify, I have added a simple loader instead of the text.
A skeleton screen is also a good idea to have.
How it works?
When you add app-shell, it adds a target to angular.json to generate index.html using SSR. During build, server will render /shell route and generated HTML is copied to index.html.
This also means, if your AppComponent is using any other components those will also get rendered in index.html
Let’s analyze this file
- App component uses Material Toolbar, which gets added to index.html
- router-outlet is preserved as it is.
- app-app-shell is the App shell component which was generated. It has Material spinner added, which is shown when the page loads. It embeds SVG inside it.
This index.html provides a app-shell (as mentioned in definition) skeleton of the app layout.
How to create production build?
ng run my-app:app-shell:production
Next steps
- Use skeleton screen in App shell component instead of loader.
- Try to build app shell just using HTML & CSS, browser can render these contents, without JavaScript.
Source code reference
Tour-of-Heroes app from Angular guide.
App without app shell
App with app shell