Angular Universal: Server-Side Rendering and Pre-Rendering for Angular 9+ Applications

Keanu William Ellwood
DVT Software Engineering
8 min readMay 27, 2020

What Is Angular Universal?

Angular Universal is a toolkit that allows us to do server-side rendering (SSR) and pre-rendering for our Angular applications.

Why Use Angular Universal?

Angular applications are single-paged apps (SPAs) - this means a single HTML document is served to the client, which can then be dynamically rewritten on the client browser-based on data received from the server, instead of serving a static page when routing. Angular Universal allows the app to be executed on the server, which lets us generate and serve static HTML to the client in response to navigation. This means that when a user opens your Angular app, the server will show the user a pre-rendered version of the app which they can browse while the client-side application loads behind-the-scenes. When the client-side application has finished loading, the application switches from the pre-rendered app to the fully interactive client-side app, without the user even noticing.

Why Use Server-Side Rendering?

  • Improve Search Engine Optimization (SEO) — Companies like Google and Facebook make use of web crawlers to understand the content of your app so that a search engine can index that content and makes it easier for users to find on the web. Most web crawlers have a hard time extracting content from SPAs, which can pose problems for SEO. Luckily, Angular Universal generates a static app, which allows web crawlers to seamlessly traverse our app.
  • Show the first page quickly — The time it takes the browser to render the first piece of DOM content (Images, SVGs, etc.) is known as First Contentful Paint (FCP). In 2019, eBay found that for each 100-millisecond improvement in search page load time, they saw a 0.5% increase to their ‘Add to cart’ count.
  • Improve performance for low-powered devices — Many devices are unable to execute JavaScript reliably, which becomes a major issue when we do a lot of scripting on the client, such as rendering a PWA. If this is the case, we might have to serve the client with a server-rendered, no-JavaScript version of the app.
A modern laptop with a code editor opened, on a white table.
Photo by Clément H on Unsplash

What’s new in Angular Universal 9?

  • Live code reload for SSR— The power of ng serve comes to SSR with Dev server! Now every time you make a change to your SSR app in development, it will automatically recompile the client and server bundles and live-reload the browser.
  • Pre-rendering out of the box — It is now possible for us to generate and save static pages to the file system at build time, which can then be served to the client. All routes can be pre-rendered during build time.
  • Easy to add — Before the 9.0 release, adding Universal to your app could be a confusing matter, with additional parameters and setup required. However, adding server-side rendering to your project is now easier than ever! Just run ng add @nguniversal/express-engine and your app is set for SSR.

Add Angular Universal To Your App

Let’s start by creating a new Angular project using the Angular CLI.

ng new universal-app
**? Would you like to add Angular routing? No
**? Which stylesheet format would you like to use? CSS
cd universal-app

Next, we install the Angular Universal schematic to set our app up for server-side rendering and pre-rendering.

ng add @nguniversal/express-engine 

If you are running an older Universal schematic and you want to upgrade, run:

ng update @nguniversal/express-engine

Comparing First Contentful Paint and Search Engine Optimization between the client-side and server-side apps.

First, we add a script inside index.html to log FCP in the browser.

Updated index.html

Let's start the client-side rendered (CSR) version of our app using npm run start, and the server-side rendered (SSR) version with npm run build:ssr && npm run serve:ssr. The CSR and SSR apps can be opened concurrently, as they load on separate ports by default. The CSR app is available on port 4200, which can be changed in package.json. The SSR app is available on port 4000, which can be changed in the generated server.ts file.

The First Contentful Paint (FCP) of the client-side app on initial load is 770ms and on subsequent reloads ~440ms. The FCP of the server-side app on initial load is 110ms and on subsequent reloads ~75ms. Already, with this simple example, we can see that the server-side app starts showing page content to the user in a fraction of the time it takes the client-side app to do the same.

We can also see how server-side rendering helps SEO, by comparing the page-source for both the client-side and server-side apps. The client-side application only shows <app-root></app-root> with nothing else inside it, whereas the server-side app displays the page CSS and all the app content coming from <app-root></app-root>. This makes it much easier for a web-crawler to scrape our app’s content.

Client-side rendered source (left) vs Server-side rendered source (right).
The SEO scores for the CSR app (left) and the SSR app (right). These scores were determined with the auditing tool, Lighthouse, which is included in Chrome DevTools.

That’s it for the basics of Angular Universal and Server-side Rendering!

Easy development of SSR apps with Dev Server

As a developer, you can now easily see the SSR app reload when making changes to the app by using the new Dev server.

npm run dev:ssr

This allows for the same fast reload capabilities we have with using npm run startwhen developing client-side apps.

Pre-render any page with Angular Universal

Angular Universal 9 brings us a powerful tool in pre-rendering. Let’s say you have a bunch of simple pages that don’t need to be rendered server-side or rendered on the client every time a user makes a request. Angular Universal 9 now allows you to cache these pages as static files, which can then be served to the client via your CDN or a simple server. How?

In your Angular application, navigate to the angular.json file and look for the prerender builder (at the very end of the default angular.json). The builder comes with a routes option, which allows you to specify the routes of the app pages you wish to pre-render.

angular.json with routes option

If you want a bunch of your app pages to pre-render, but you don’t want to provide all the routes manually, you can change the routes option to the routesFile option. The routesFile option allows you to point to a relative url that contains all the predefined routes for the pages you want to pre-render at build time. You can fill this file with the desired routes using a script or any other method you prefer.

pre-render-routes.txt
angular.json with routesFile option

Then just build the app and serve!

npm run prerender
npm run serve:ssr

You can find the pre-rendered files in your dist/<project-name>/browser . If you want to verify that your page has been pre-rendered just navigate to localhost:4000/<route-name>/index.html or if you are using a cdn just navigate to the route and inspect the page-source. At the bottom of the page you will see a small comment.

This page was pre-rendered with Angular Universal

Pre-rendered page source.

When To Use Server-side Rendering and Pre-Rendering

If you have decided to implement Angular Universal into your application, you might want to consider which approach is better for the current situation.

When to use Server-side Rendering?

  • The application or web pages you want to render on the server are very dynamic.
  • If you have content fetched asynchronously on pages where SEO is important.

When to use Pre-Rendering?

  • The route you want to pre-render is more-or-less completely static.
  • If your application does not need to be updated frequently, you can run the build script after you’ve made changes to the static pages and just publish the /dist folder again.

Other Angular Universal Packages

Angular Universal has been integrated with ASP.NET Core. The ASP.NET Core Engine can be used for running Angular Apps on the server for server-side rendering. ASP.NET Core Engine

The Angular Universal Socket Engine provides Angular Universal rendering for any language, framework or platform you choose to use. As long as it can connect to a TCP Socket (which all frameworks can) then you’re good to go.

The Angular Universal team has also provided a Hapi Engine package for running Angular Apps on the server for server-side rendering. Hapi (Hapi.js) is most commonly used to build API servers, websites, and HTTP proxy applications.

You can find all other available Angular Universal packages on https://github.com/angular/universal

Conclusion

As we have seen, server-side rendering is mainly used to improve the load time of our applications. It does this by converting HTML files on the server into static application pages, which is then sent to the browser where it can be bootstrapped on the client.

Most web crawlers are unable to properly scrape content on apps that rely heavily on JavaScript, thus server-side rendering is a must if you are optimizing your Angular app for SEO. Server-side rendering allows web crawlers to scrape app content and index that content to make it searchable on the web, and can provide faster pages, which in turn can give your app an SEO ranking boost.

I hope that this post was of help to you if you were looking to integrate server-side rendering or pre-rendering into your Angular application.

--

--

Keanu William Ellwood
DVT Software Engineering

DVT Software Engineer Graduate | Front-End Graduate. Software Engineering student at The Belgium Campus.