Disabling Angular when statically generating with Scully

Disabling Angular when statically generating with Scully

Posted on
Last update on

Scully - a Static Site Generator for Angular series

This article is part of a growing series about Scully - a Static Site Generator for Angular. If you get excited about this article be sure to check the others!

  1. Custom plugins for Scully - Angular Static Site Generator
  2. Disabling Angular when statically generating with Scully
  3. Scully or Angular Universal, what is the difference?
  4. Scully and Angular Universal, comparing benchmarks

Target audience

Scully, the Static Site Generator for Angular, renders your Angular application into static HTML files. Together with the bundled JS assets of the Angular app, those files are served to the browser. The static HTML files give your visitors an instant view on the content of the application.

In the background the Angular application is bootstrapped and takes over the static page, enhancing it again as a Single-Page-Application.

But do you really need to bootstrap Angular again? Perhaps not! If you care about the performance of your statically generated Angular application or website, please continue reading. If you still need to learn about the basics of Scully, check my first article about custom plugins for Scully.

Disabling Angular? Wait, what?

Reading the title of this article might confuse you. We are not actually completely disabling Angular, because we are still building the application using Angular.

We are questioning whether or not we still need Angular in the browser of our visitor. Because when our application is built, our pages will get statically prerendered with Scully and delivered AS-IS, with all the available content, to the browser.

The main goal of the framework, wiring all components and services together into a nice page, has already happened during our build step! In this case, the framework becomes more of a build tool then a SPA framework.

Why disable Angular?

No matter how good you optimize your application, the whole set of static assets of your Angular app is still hundreds of kilobytes. If you don't need them to build the page, there is no point loading all these assets in the browser.

Prerendering the app improves the performance of your application. By not bootstrapping the page as an Angular app we can increase the performance of our application even more!

Example project

To show to what extent we can improve the performance, we use an example application. The example project is a simple Angular v9 application with a few static routes, like /about, and dynamic routes like /news that lists the available newsitems and /news/:id/:slug that shows the detail of a newsitem. The list of newsitems is retrieved via HttpClient from a static resource /assets/news.json and the news detail is available at /assets/news/{:id}.json.

The application is always built with AOT enabled. The page we will use to measure the performance will be a newsdetail page, of which the example is shown below:

This page will be audited in the following scenarios:

  1. Client side rendered (CSR) version of the app.
  2. Post-built prerendered version of the app
  3. Post-built prerendered version of the app, but without loading the Angular statics assets

There are different tools to audit the performance of your application, but a good starting point is Lighthouse. We will be using Lighthouse via the Chrome developer tools. The configuration options for the audit are:

  1. Device: Mobile, Clearing storage
  2. Simulated Slow 4G, 4x CPU Slowdown

CSR version

In this version we are loading the Angular application on the newsdetail page the typical way. The server returns all the static assets together with the base index.html to the browser. Angular bootstraps on the client and performs its magic to show the newsdetail page. Running the Lighthouse audit gives us the following result:

As you can see the performance score for this version is unacceptable low at 69. We can definitely do better.

Post-built prerendered version

In this version the same newsdetail page is prerendered with Scully and returned by the server together with all the static assets that form the Angular application. Angular bootstraps again on the client as a Single Page Application. Now our audit result looks like this:

This is already much better. Having a score of 90 on performance will put you in the segment of high performing websites and applications. But still, we can do better..

Post-built prerendered version, without Angular static assets

In this version the same newsdetail page is prerendered with Scully and returned by the server, but now without all the static assets that form the Angular application. Angular is not bootstrapping, but our page can be read and used because all the initial content is there!

Well, this is the maximum. Not possible to do better :) Mission accomplished! Not hitting 100 is no disaster, but you should end up above 90 anyway!

How to disable Angular?

Disabling Angular can be done by removing the <script> tags that load the static assets to bootstrap the Angular application. Based on the assets.json or assets-es2015.json file that gets generated when building the application we can parse the generated HTML and remove the tags.

Not sure how to get started? There is a community plugin available for Scully that will do the work for you. Install the plugin using npm i -S scully-plugin-disable-angular and add the plugin to your Scully configuration file:

scully.<your-project-name>.config.js

const {MinifyHtml} = require('scully-plugin-minify-html');
const {DisableAngular} = require('scully-plugin-disable-angular');

const postRenderers = [DisableAngular, MinifyHtml];

exports.config = {
  projectRoot: './src',
  defaultPostRenderers: postRenderers,
  routes: {
    '/news/:id/:slug': {
      type: News,
      url: 'http://localhost:4321/assets/news.json'
    }
  }
};

Running npm run scully will now generated the static version of all the discovered routes and remove all the relevant <script> tags.

Optional: remove the dynamic state

Update: Recently I added a new option removeState to the DisableAngular Scully plugin.

When generating dynamic pages with Scully the data that was used to generate the pages is included in the HTML. This allows Scully to fetch the data from there when navigating around the pages in the bootstrapped Angular application.

However, when disabling Angular this has no use, as all navigations around the app are new navigation requests in the browser. Removing the dynamic state completely further optimizes our pages (decrease the total HTML size).

scully.<your-project-name>.config.js

const { setPluginConfig } = require('@scullyio/scully');
const {DisableAngular} = require('scully-plugin-disable-angular');

setPluginConfig(DisableAngular, {
  removeState: true
});
const postRenderers = [DisableAngular];

exports.config = {
  projectRoot: './src',
  defaultPostRenderers: postRenderers,
};

Typical use cases and examples

This technique is ofcourse not suitable for all Angular applications, becomes some rely heavily on functionality of the framework to make the application work nicely.

However, if you are building a public facing website, for example a news website, or personal website / blog, you will most probably not use very advanced parts of Angular. You will use Angular just because you like it and you have experience with it, because there are many other proven tools / frameworks available to build such a typical website.

Using Angular to hide/show a part of your application when clicking a button might be overkill. For this we could use plain JavaScript. So it might be worth investigating seperating this logic out from your Angular application.

Conclusion

Disabling Angular after prerendering it with Scully has some nice added advantages. We don't transfer unnecessary bytes over the wire and our pages are much faster and get better scores from auditing tools like Lighthouse.

This technique is not suitable for all Angular applications, because we might need some functionality of the framework when our application is rendered in the browser. If that is the case, and the functionality is limited, consider moving this logic into a separate JavaScript file.

Further reading

  1. Scully - Github organization
  2. Scully - live code introduction on Youtube
  3. Scully, the First Static Site Generator for Angular
  4. Create a Static Site Using Angular & Scully
  5. Disable Angular after prerender - Scully plugin
  6. Minify the HTML of your prerendered Angular application - Scully plugin

Special thanks to

for reviewing this post and providing valuable and much-appreciated feedback!

Contents

  1. Introduction
  2. Target audience
  3. Disabling Angular? Wait, what?
  4. Why disable Angular?
  5. Example project
  6. CSR version
  7. Post-built prerendered version
  8. Post-built prerendered version, without Angular static assets
  9. How to disable Angular?
  10. Optional: remove the dynamic state
  11. Typical use cases and examples
  12. Conclusion
  13. Further reading
  14. Special thanks to

By reading this article I hope you can find a solution for your problem. If it still seems a little bit unclear, you can hire me for helping you solve your specific problem or use case. Sometimes even just a quick code review or second opinion can make a great difference.