Angular Universal & Server-side rendering Deep-Dive
If you’ve been interested in, or looking for information on Angular Universal, you might of had a hard time getting started as there has been many changes over the past few years, and a lot of dated or general misinformation out there!
Let’s change all of that, and get you up to speed, fast!
In this multi-part series, we’re going to look at:
- Getting setup with Angular Universal
- The Architecture and behind-the-scenes of SSR.
- Gotchas, Tips & Tricks of getting even the largest enterprise application running with Angular Universal.
NOTE: Previously Angular Universal was known as
angular2-universalon npm, etc. Make sure to ignore any articles or code based on that, since it’s been long deprecated !
Part 1: Server-side Rendering with Angular
Angular Universal is simply the nickname for Server-Side Rendering (or SSR for short) with Angular, technically the package is now found under
@angular/platform-server. This is in thanks to a lot of great work by PatrickJS, Jeff Whelpley & Angular Core team members Vikram Subramanian & Alex Rickabaugh (many others as well) 👏
So now that we understand a bit of the past & present, let’s dive right into how you can get started server-rendering your Angular applications right now!
Part 1 — Getting started with Angular Universal and the Angular-CLI
I’d highly highly recommend starting off with the recent integration Universal has with the Angular-CLI.
First, let’s create a new Angular-CLI Project:
Make sure you have the latest Angular-CLI
npm i --g @angular/cli
Note: you could skip this and go straight to trying to add Universal to your existing CLI application if you’d like.
ng new some-amazing-project
Now let’s add Universal 🍹
As of Angular-CLI version 6.1, there has been an
ng add schematic added that allows you to quickly create the scaffolding, Node express server, and other wiring needed to get you started.
✨ Kudos to @Caerusaru for getting this setup!
Angular Universal schematic installation
cd into your root Angular-CLI project directory you’d like to add Universal into, and run the following schematic.
--clientProject [name]portion. Take a look at your angular.json file, and make sure you put the “name” of the project you’re trying to add server-side rendering to.
ng add @nguniversal/express-engine --clientProject [name]
In the example angular.json above, we’d want to use:
ng add @nguniversal/express-engine --clientProject some-amazing-project
Did we just add all the Universal setup & Node server to our application in 1 line of code?!
Time for a promotion 🏁 🔮
How do we run SSR for our Angular App?
Typically we’d run
ng serve to get our normal Client-side rendered (CSR) application fired up…
When we want to build and run our server-side rendered version, we’re going to have to use a few scripts that were added to our package.json.
Before we dive into those important scripts, I want to cover 2 different crucial topics in the server-side rendering world (regardless of JS Framework / etc)…
Dynamic SSR & Static Pre-rendering
Dynamic SSR is the concept that there will be a live Node server spun up that whenever a Route is hit, it will dynamically generate and serialize the application — returning that String to the browser.
Static Pre-rendering is when we want to pre-render a list of routes, and create static files, (ie: index.html, about-us.html, etc) and then use a server of our choosing to serve up those files later on.
So how do we know which one to choose and when?
Pre-rendering will typically give you better performance since we’re not waiting for a server to hit all the necessary APIs within your application, and nothing has to be “serialized”, it already has all the serialized HTML of your application outputted for each one of the Routes files.
Here’s a good list of questions that we consider with clients before considering which route we need to take.
When to use Static Pre-Rendering:
- Your application itself is rather Static.
(or at least the Routes you’re trying to pre-render)
For example: homepage | about us | contact us
- Very dynamic portions of your site (and ones that are behind a Login/Auth barrier) can be pointed to the normal Client-side rendered (CSR) version of your application, and Angular can bootstrap itself normally.
- Your application doesn’t update very often.
Whenever some changes are needed on the static routes, you can simply run the build script again and republish the
/distfolder containing all of your pre-rendered files.
When to use Dynamic SSR:
- Your application (and the routes you need to SSR) are very dynamic 🃏
- You have a list of “trending products” | “live data” | etc, that you need populated correctly for every server-side render.
- Your applications structure is rendered based on JSON files or a CMS where anything could change at any given moment.
Typically most applications will need static pre-rendering (since any routes behind an authentication-wall don’t gain much/any benefit from utilizing SSR, since one of the main purposes is the SEO gains, and improved perceived performance.
Remember, you can always have certain aspects of your application not render during SSR, and have those dynamic portions populated during CSR! We’ll get into that in upcoming articles in this series.
How to start up Angular Universal
Going back to our package.json, we can see we have a few new scripts that were added. There were many new scripts added, but if you take a look below — these will give you both methods of SSR right away!
Note, you could also shorten these scripts into a script that runs both so it’s even easier for you.
// Dynamic SSR
npm run build:ssr && npm run serve:ssr
This will compile your application and spin up a Node Express server to serve your Universal application on
// Static Pre-Rendering
npm run build:prerender && npm run serve:prerender
This script compiles your application and pre-renders your applications files, spinning up a demo http-server so you can view it on
Note: To deploy your static site to a static hosting platform you will have to deploy the
dist/browserfolder, rather than the usual
So how do we know it worked?
Depending on which version (dynamic/static) you fired up, if you access that localhost URL, you should now see that the typical empty
<app-root></app-root> will now have content inside it! ✨ ✨
You can set your
<title>My Application is amazingggg</title> .
You can set your
Your application content will be serialized.
The world is your oyster. 😎
I hope this has helped demystify Angular Universal / Angular SSR for you, if only a little bit. There’s plenty more we’ll be talking about in upcoming articles, so stay tuned!
Manually setting up Angular Universal:
If you’d prefer to add everything yourself, take a look at the CLI Wiki which describes (step-by-step) how to add all the individual pieces to a CLI project:
Also you can take a peek at the Angular Universal-Starter repo, which is the end-result of the steps shown in the Wiki. All the new files that are needed, setting up the basic Node server, etc.
ASP.NET Core & Angular Universal integration?
Angular Universal has also been integrated with ASP.NET Core (where it itself invokes a small Node process to handle the server-side rendering, piping all of the data back to ASP.NET and rendering it).
ASP.NET Core & Angular Universal repo.
We’ll do a deep-dive into how that is all setup in another article in this series!
See more Universal add-ons & libraries here: https://github.com/angular/universal
(Angular-CLI Hapi schematics, and more ASP.NET integration tools coming soon…)
Stay tuned for Part 2 & 3 coming soon !
We’re going to take a much deeper look into:
- Internal Angular SSR architecture and deep-dive
- SSR “Gotchas”
- Tips & Tricks
- Node performance optimization tips
- ASP.NET Core integration & tutorial.
- Much more…