How to Use Angular Universal for Server-Side Rendering

Brief: Angular Universal enables server-side rendering (SSR) in Angular apps, improving performance and SEO. This guide walks you through setting up Angular Universal with routing. You will learn how SSR works under the hood with practical examples and how to boost user experience with faster load times and pre-rendered content.
Introduction to Angular Universal and Server-Side Rendering
Angular Universal is a powerful tool that allows you to perform server-side rendering (SSR) for Angular applications. SSR enhances the performance of your application by pre-rendering the HTML on the server, which is particularly beneficial for improving SEO and the initial load time of your application.
Benefits of Angular Universal for SSR
Improved Performance: Faster initial load time as the server sends the pre-rendered HTML.
SEO Optimisation: Search engines can easily crawl fully rendered pages.
Better User Experience: Reduced time to be interactive for users, especially on slower devices.
Setting Up Angular Universal with Angular CLI
Step 1: Create an Angular Application
If you don’t already have an Angular application, create one using the Angular CLI:
ng new angular-ssr-app
cd angular-ssr-appStep 2: Add Angular Universal
Add Angular Universal to your project by running the following command:
ng add @nguniversal/express-engineThis command configures your project for SSR with Angular Universal and Express. It creates a server.ts file and updates your project files accordingly.
Adding Routing to Your Angular Application
Step 1: Generate Components
Generate some components to use with routing. For example, create home and about components:
ng generate component home --module=app.module
ng generate component about --module=app.moduleStep 2: Set Up Routing
Update your app-routing.module.ts to define routes for the home and about components:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
const routes: Routes = [
{
path: '',
component: HomeComponent,
},
{
path: 'about',
component: AboutComponent,
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {
initialNavigation: 'enabledBlocking',
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}Step 3: Update Your App Component
Update your app.component.html to include links to the routes and a router outlet:
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>app.component.scss:
/* Styles for the navigation bar */
nav {
background-color: #2c3e50;
padding: 10px;
text-align: center;
}
/* Styles for the navigation links */
nav a {
color: #ecf0f1;
text-decoration: none;
font-size: 1.2em;
margin: 0 15px;
padding: 5px 10px;
border-radius: 5px;
transition: background-color 0.3s ease;
}
/* Hover effect for navigation links */
nav a:hover {
background-color: #34495e;
}Step 4: Create the Home and About Components
Home Component
home.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent {}home.component.html:
<div class="home-container">
<h1 class="home-title">Welcome to the Home Page!</h1>
<p class="home-content">
This is the home page of the Angular Universal application.
</p>
</div>home.component.scss:
/* Add any specific styles for the home component here */
.home-container {
text-align: center;
padding: 20px;
}
.home-title {
font-size: 2.5em;
color: #2c3e50;
margin-bottom: 20px;
}
.home-content {
font-size: 1.2em;
color: #34495e;
}About Component
about.component.ts:
import { Component } from '@angular/core';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss'],
})
export class AboutComponent {}about.component.html:
<div class="about-container">
<h1 class="about-title">About Us</h1>
<p class="about-content">
This is the about page of the Angular Universal application.
</p>
</div>about.component.scss:
/* Add any specific styles for the about component here */
.about-container {
text-align: center;
padding: 20px;
}
.about-title {
font-size: 2.5em;
color: #27ae60;
margin-bottom: 20px;
}
.about-content {
font-size: 1.2em;
color: #2ecc71;
}Step 5: Update Server Configuration
Ensure that your server.ts file correctly handles requests for all routes. The initial setup should cover this, but let's go through it for clarity.
import 'zone.js/node';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { AppServerModule } from './src/main.server';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/angular-ssr-app/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html'))
? 'index.original.html'
: 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModule,
})
);
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get(
'*.*',
express.static(distFolder, {
maxAge: '1y',
})
);
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, {
req,
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
});
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = (mainModule && mainModule.filename) || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';Excellent Angular Development Services
AnguEmploy lar Minds is a promising Angular web development company that has more than 14 years of experience in building scalable projects.
How to Run an Angular Project With Ssr?
Step 1: Build the Application
First, build the client-side app, and the server-side render of parts of the client application with:
npm run build:ssrThis command will create two folders: dist/angular-ssr-app/browser and dist/angular-ssr-app/server.
Step 2: Serve the Application
To serve the application using Angular Universal:
npm run serve:ssrYou should see the output indicating a few new scripts that the server is running:
Node Express server listening on http://localhost:4000Open your browser and navigate to http://localhost:4000 to see your server-rendered Angular application with routing.
Example Walkthrough
Home Page (/):
User navigates to http://localhost:4000/.
The server receives the request and renders the HomeComponent.
The server sends back fully rendered HTML for the HomeComponent.
A client receives and displays the HTML immediately.
Angular initializes on the client side and takes over for interactive elements.
About Page (/about):
User navigates to http://localhost:4000/about.
Server receives the request and renders the AboutComponent.
Server sends back fully rendered HTML for the AboutComponent.
Client receives and displays the HTML immediately.
Angular initialises on the client side and takes over for interactive elements.
How SSR Works
Understanding SSR in Angular Universal
Server-side rendering in Angular Universal works by pre-rendering the Angular application on the server before sending it to the client. Here’s a step-by-step breakdown of the whole angular server-side rendering to client-side rendering process:
Initial Request: When a user makes an initial request to the server, the server uses Angular Universal to bootstrap the Angular application.
Pre-rendering: The Angular application runs on the server, rendering the components and generating the HTML content.
Sending HTML: The server sends the fully rendered HTML to the client's browser.
Client-Side Bootstrapping: Once the HTML is loaded, Angular bootstraps on the client side, taking over from the static HTML and making the application interactive.
This process ensures that the user sees a fully rendered page almost immediately, improving both search engine performance and SEO.
Real-World Example
Let's consider a real-world example to showcase the benefits of SSR. Imagine you have an e-commerce website built with Angular.
Scenario Without SSR
When a user visits your homepage, their browser downloads the JavaScript bundles.
The Angular application runs in the browser and fetches data from the server.
The page renders after all these steps, which could take a few seconds on a slow connection.
Scenario With SSR
When a user visits your homepage, the server pre-renders the Angular application.
The user immediately sees the fully rendered HTML content.
Angular bootstraps in the background, making the page interactive without the user noticing any delay.
Benefits of This Scenario
Faster Load Time: Users see the content immediately, reducing bounce rates.
Better SEO: Search engines can easily crawl and index the pre-rendered HTML.
Improved Performance: Especially on mobile devices and for users with slower internet connections.
Improved User Experience: Users get a faster time to first paint and interactivity, leading to higher engagement and satisfaction.
Advanced Topics in Angular Universal
Fine-Grained Universal Application Shell
Implement a fine-grained Universal Application Shell to render content on the server selectively
This improves performance by only rendering what is necessary
Integrating Angular Universal with ASP.NET Core
Angular Universal has also been integrated with ASP.NET Core (where it invokes a small Node process to manage the server-side rendering, piping all of the data back to ASP.NET and rendering it)
Resources for Further Learning on Angular Universal and SSR
Visit Angular Tutorials to learn fundamental and advanced Angular knowledge from our Angular experts
Check out the Trilon Blog for more articles on Angular, NestJS, and much more!
Conclusion and Next Steps
This tutorial covered how to set up Angular Universal for server-side rendering (SSR) with routing in your Angular application. SSR offers significant benefits such as improved performance, SEO, and user experience by pre-rendering your application on the server.
We walked through the steps of adding Angular Universal to your application code in the app root of the Angular app in your project, setting up routing, creating components, and configuring the server. Additionally, we discussed how SSR works under the hood and provided a real-world example to illustrate its advantages.
By implementing Angular Universal with SSR, you've taken a crucial step towards optimizing your Angular application for better performance and SEO. Feel free to explore customization options, optimize your application for web development and production, and learn about how Angular Universal works to leverage its full potential.
FAQ's
