Server Side Rendering (SSR) in Angular

Shahjalal
Oceanize Lab Geeks
Published in
9 min readJul 9, 2020
Server site rendering

[Note: currently I have learned the server site rendering and I am writing this article which I have learn. Please let me know if there are any mistakes or if i write any wrong information]
Angular application executes in the browser and rendering the pages in the DOM by user actions. In this article we will learn how to implement server site rendering in angular application very easy and simplest way. So Let’s start,

What is Server Side Rendering (SSR)?

Server-side rendering (SSR), is the ability of an application to contribute by displaying the web-page on the server instead of rendering it in the browser. Server-side sends a fully rendered page to the client; the client’s JavaScript bundle takes over.

Why we use Server Side Rendering (SSR)?

There are 3 reasons for create angular application with server side rendering.
1. Search engine optimization (SEO).
2. Improve performance on web and mobile.
3. load the page quickly.

Most of web applications are used Client Side Rendered, that means all the necessary codes (HTML, CSS, JavaScript and others) are make bundled together and transfer to the client browser at once. It’s Depending on browser URL, frameworks like Angular, React, Vue etc.uses that code to show (browser url) different views by manipulate DOM and making network requests. In this approach user wait long time before downloaded all necessary information in client browser this user see the blank page or loader.

Basically, In angular index.html page is served from express server for all the URL paths and that index.html page is passed through some express view engine which injects HTML into <app-root></app-root>, based on current route and component for that route.

So, Now I will explain in step by step to implement server site rendering in angular with simple example. One thing, we use angular cli version. So please Make sure you have Angular CLI 1.6+ or latest version installed.

Step 1: Install the Angular CLI 1.6 or updated version

Install the angular cli globally using npm, open a terminal or console using following command for creating angular project. The command are bellow Note: If already installed angular cli then skip step 1.

npm install -g@angular/cli

Step 2: Create Simple Application

At First, we create an angular application with routing. We use routing because we want show to show you how server side rendering works for different URLs, we are going to import a routing module. To create an angular application with routing module automatically generated and scss style support.
For creating new project with necessary information will run the command

ng new angular-ssr-practice--style scss -routing

This will create angular_ssr_practice folder, where our angular code will be located. Let’s open that in sublime, vscode or your favourite code editor.

Now Let’s understand how server side rendering works. Traditionally, we used to serve entire dist folder (technically, only index.html file from dist folder) which is contains build files of our angular application. But now, we are going to create an express server which serves index.html file from dist folder.

Later, browser has the completely rendered HTML but the application will re-render (by bootstrapping) once all necessary JavaScript and CSS files are downloaded.

Step 1: Create few Component for production ready

Already, we have an angular application. Now, we are going to create few components with following commands.

ng g c components/home — module app.module.ts
ng g c components/about — module app.module.ts
ng g c components/contact — module app.module.ts

Above command, will generate home, about and contact components inside
src/app/components folder. After complete, we will import these components inside routing module which will be app-routing.module ts inside src/app folder.

<!-- app-routing.module.ts -->import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './components/home/home.component';
import { AboutComponent } from './components/about/about.component';
import { ContactComponent } from './components/contact/contact.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
ContactComponent
],
imports: [
BrowserModule,
BrowserTransferStateModule,
CommonModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})export class AppModule { }

Step2: Change generate component like home,about and contact

In before, we are implementing different components for different routes, we need to add a router-outlet inside app.component.html and add some html code in app component as well.

Update app component in app/app.component.html

<!-- app.component.html -->
<div class="content" role="main">
<div class="card-container">
<div class="card card-small" tabindex="0">
<a routerLink="/" routerLinkActive="active [routerLinkActiveOptions]="{exact: true}">Home</a>
</div>
<div class="card card-small" tabindex="0">
<a routerLink="/about" routerLinkActive="active">About</a>
</div>
<div class="card card-small" tabindex="0">
<a routerLink="/contact" routerLinkActive="active">Contact</a>
</div>
</div>
<router-outlet></router-outlet>

Update home component app/components/home.component.html and app/components/home.component.ts

<!-- home.component.html --><h2>Welcome to home section</h2>
<p>
Angular application executes in the browser and rendering the pages in the DOM by user actions.
In this article we will learn how to implement
server site rendering in angular application very easy and simplest way. So Let’s start,
</p>
<!-- home.component.ts -->
import { Component, OnInit } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(
private title: Title,
private meta: Meta
) { }
ngOnInit(): void {
this.title.setTitle('Home / Angular SSR Implementing');
this.meta.updateTag({
'description': 'Angular SSR Implementing'
});
}
}

Now we change about component in this file about.component.html and about.component.ts

<!-- components/about.component.ts -->
<h2>Welcome to about section with some user info add</h2>
<div class="">
<div *ngFor="let user of users">
<p> User Name:{{ user.name }} </p>
<p>Address: {{user.address.street}} {{user.address.suite}} {{user.address.city}} {{user.address.zipcode}}</p>
<p> User Email:{{ user.email }} </p>
<hr>
</div>
</div>
<!-- about.component.ts -->import { Component, OnInit } from '@angular/core';
import { Title, Meta} from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {
public users: any = [];
constructor(
private title: Title,
private meta: Meta,
private http: HttpClient,
private state: TransferState
) { }
ngOnInit() {
this.title.setTitle('About Angular SSR title update');
this.meta.updateTag({
'description': 'About Angular SSR description update'
});
this.http.get('https://jsonplaceholder.typicode.com/users')
.subscribe((users) => {
this.users = users;
this.state.set(STATE_KEY_USERS, <any>users);
}, (err) => {
console.log(err);
});
}
}

Similarly, in about component, we have changed the meta tags but we are importing users data from a remote demo server. This data will be dynamically injected inside <div> element on the client side but we expect it to be available (already rendered) in the response from the server.
Finally, we are done all thing in our application at the moment,

Now we will run the bellow command

npm run build

When complete the command and open the project in your favourite code editor then will see the dist folder was create inside the application and also we see some build file
Now we can preview it by running command ng serve and view available at http://localhost:4200 server. The screen look like

View

Note: In about component we call external API using httpclicent. For that make sure you have HttpClientModule module imported in app.module.ts, else application won’t work.

Now we click on different menu buttons to see the views are changing, We can also see meta-tags changing. But it we see the source using ctrl+u or view page source option in context menu of the browser, we can only see index.html file with empty app-root.

There is no difference between angular application without server side rendering and with server side rendering when seen from client side. But if you see source for both home and about routes, you can see meta tags are changed and application is fully rendered.

If inspect browser then you can see the changes in home and about where meta tag is different
Home Route:

Home route

About Route:

About route

Step 3: Implement State transfer

If we visit http://localhost:4200/about page, then express server will send a HTTP request to get users data and return the rendered HTML. While on browser, the same request will be made again. This is not good for user experience and will create many problems. For overcome this problem we used `state transfer` in angular details here State Transfer

So, we can set state of an application before that application bootstraps. This is done by BrowserTransferStateModule and ServerTransferStateModule modules.

These modules helps angular transfer state of application bootstraps on server to be transferred to the application that runs on the browser.

Now, next step is to prevent HTTP request if application state already has a key which contains some data. If that data is present, then we don’t need to make HTTP request. TransferState service provides an interface to interact with application state. Hence we need to inject it inside about.component.ts. It is available inside platform-browser module.

For state transfer we need update some code. Those here
<! — app/component/about.component.ts →

<!-- app/component/about.component.ts -->
import { Component, OnInit } from '@angular/core';
import { Title, Meta, TransferState, makeStateKey } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
// make state key in state to store users
const STATE_KEY_USERS = makeStateKey('users');
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {
public users: any = [];
constructor(
private title: Title,
private meta: Meta,
private http: HttpClient,
private state: TransferState
) { }
ngOnInit() {
this.title.setTitle('About Angular SSR title update');
this.meta.updateTag({
'description': 'About Angular SSR description update'
});
this.users = this.state.get(STATE_KEY_USERS, <any>[]);
if (this.users.length == 0) {
this.http.get('https://jsonplaceholder.typicode.com/users')
.subscribe((users) => {
this.users = users;
this.state.set(STATE_KEY_USERS, <any>users);
}, (err) => {
console.log(err);
});
}
}
}

In app.module bellow line

import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';
import { HomeComponent } from './components/home/home.component';
import { AboutComponent } from './components/about/about.component';
import { ContactComponent } from './components/contact/contact.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
AboutComponent,
ContactComponent
],
imports: [
BrowserModule,
BrowserTransferStateModule,
CommonModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Create new file in /src for state transfer browser.app.module.ts paste bellow code

<!-- browser.app.module.ts -->import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppModule } from './app/app.module';
import { AppComponent } from './app/app.component';
@NgModule({
imports: [
AppModule,
BrowserModule.withServerTransition({ appId: 'ssr-example' }),
],
bootstrap: [AppComponent]
})
export class BrowserAppModule { }

And Update main.ts in app directory

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { BrowserAppModule } from './browser.app.module';
import { environment } from './environments/environment';
import { from } from 'rxjs';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(BrowserAppModule)
.catch(err => console.error(err));

TransferState provides get method to get a state value. First parameter to get method should be a key in the state, of type StateKey. Hence need to make a key using function makeStateKey which is done in line no. 6. Second parameter of get function is default value if key does not exist in state which we set to empty array.

Later, we are checking if this.users is empty array or not, done on line no. 31. If array is empty, then make HTTP request for the users data and set the state value which is done on line no. 35. Later, when browser bootstraps the application, users data will be already in the state and it won’t have to make another HTTP request.

After this change, rebuild the application and restart express server. Now, browser should not make another HTTP request as state object will available when angular application bootstraps on the server.

After this change, rebuild the application and restart server. Now, browser should not make another HTTP request as state object will available when angular application bootstraps on the server and also if we click menu tab the browser don’t make another http request.

Now clear the network request and click the menu I mean home and about you can see the browser don’t make another http request. Example screen is bellow.

I have made an example GitHub repository with all codes implemented in this article Link

--

--