Getting Started with Angular Universal


Angular Universal builds on Angular‘s widespread adoption and support to provide server-side rendering and faster perceived performance to Angular applications. By pre-rendering an application at build time, or re-rendering at run time, web applications using Angular Universal benefit from better site previews, search engine optimization, and quicker page loads. Unlike traditional server-rendered strategies (JSPs, etc), Angular Universal is more of a hybrid approach. The initial page is rendered by the server, while the client-side (traditional Angular) application is loaded in the background. Once the client-side application is ready, it takes over and does all of the work from there. Therefore Angular Universal benefits from the initial load speeds of server rendering and the user experience of a client-side application. This post will illustrate the  basic usages of Angular Universal and its integration with an Angular 2 application.

Getting Started

We will begin with the starter application provided by the angular universal team. The source code can be found here. This application has Angular Universal already setup and ready to go. Prerequisites are git and node.

git clone
cd universal-starter
npm install
npm start

Navigate to http://localhost:3000 to see the running application!

Setup Explained

For the bulk of the Angular Universal configuration, visit main.node.ts

// Application
import {App} from './app/app.component';
import {routes} from './app/app.routes';
export function ngApp(req, res) {
 let baseUrl = '/';
 let url = req.originalUrl || '/';
 let config: ExpressEngineConfig = {
   directives: [
   platformProviders: [
     {provide: ORIGIN_URL, useValue: 'http://localhost:3000'},
     {provide: APP_BASE_HREF, useValue: baseUrl},
   providers: [
     {provide: REQUEST_URL, useValue: url},
   async: true,
   preboot: false
 res.render('index', config);

This is the main configuration for Angular Universal in conjunction with Express. The main application component ‘App’ is specified in the directive array. The base url http://localhost:3000 is set to the Origin with the APP_BASE_HREF set to the root of the application. This means that angular universal will take control at the root of the application rather than at a sub-path. The router is set to Angular’s default router with the client-side routes defined in app.routes.ts. Finally, async and preboot are set to true and false respectively. We will touch on their significance later.

Then, visit server.ts  to tie the Angular Universal config to the server.

import { expressEngine } from 'angular2-universal';
// Express View
app.engine('.html', expressEngine);
app.set('views', __dirname);
app.set('view engine', 'html');
import { ngApp } from './main.node';
// Routes with html5pushstate
// ensure routes match client-side-app
app.get('/', ngApp);
app.get('/about', ngApp);
app.get('/about/*', ngApp);
app.get('/home', ngApp);
app.get('/home/*', ngApp);

These two snippets from server.ts first load the Angular Universal expressEngine, then setup each of the routes with the configuration file from above. That’s all that is needed to configure Angular Universal into the application!

Page Load

Using the default server-side rendering with Angular Universal, we get the following initial page load of the starter application:


With Angular Universal, the first response will be rendered on the server. Meanwhile, the client-side application will download/bootstrap and take over when it is ready. This means that the user is immediately presented with a usable web app. Therefore, all web-crawlers, social media previews, etc. will see the fully loaded site.

After disabling Angular Universal, we get the following initial page load:


The index.html page, which shows a loading message, is shown while the client-side application downloads and bootstraps. Although a user might know to wait for the application to load, a bot might mistakenly assume that this is the fully-loaded page.


Preboot is a toggled feature of Angular Universal. When enabled, Preboot takes over during the time after the server-rendered view is displayed, but before the client-side application is fully bootstrapped. Ideally, the client-side application should handle all of the user interaction with the web application, but for a brief period of time, only the server-rendered view is available. During this time, Preboot will record all of the user’s interactions and play them back to the client-side application when it is ready. This ensures that no interactions with the web app are lost.


Another feature of Angular Universal is async. Using the power of Zone.js, Async keeps track of all the asynchronous events that occur during the server rendering, so that it can serve the page when they are all completed.


Angular Universal can provide better perceived performance for a little bit of added complexity in an Angular application. At this point Universal should only be considered by applications where initial response time or social media sharing are paramount. Angular Universal, along with most Angular 2 tools, is still in its infancy and unfortunately, the current scope of Angular Universal only applies to Angular 2 applications using a Node backend. Overall, it seems like the project is well-maintained and I believe that the documentation and adoption of Universal will continue to grow along with Angular 2.

About the Author

Jake Partusch profile.

Jake Partusch

Sr. Consultant

Jake is a passionate web developer, a relentless tester, and a hobby hardware hacker. Jake has been a full-stack developer for over 4 years and has developed applications using Java, Spring, and AngularJS.

Leave a Reply

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

Related Blog Posts
Android Development for iOS Developers
Android development has greatly improved since the early days. Maybe you tried it out when Android development was done in Eclipse, emulators were slow and buggy, and Java was the required language. Things have changed […]
Add a custom object to your Liquibase diff
Adding a custom object to your liquibase diff is a pretty simple two step process. Create an implementation of DatabaseObject Create an implementation of SnapshotGenerator In my case I wanted to add tracking of Stored […]
Keeping Secrets Out of Terraform State
There are many instances where you will want to create resources via Terraform with secrets that you just don’t want anyone to see. These could be IAM credentials, certificates, RDS DB credentials, etc. One problem […]
Validating Terraform Plans using Open Policy Agent
When developing infrastructure as code using terraform, it can be difficult to test and validate changes without executing the code against a real environment. The feedback loop between writing a line of code and understanding […]