First, we need to create a angular app with routing. Routing is not necessary, but just to show you how server side rendering works for different URLs, we are going to import a routing module. To create a angular app with routing module automatically generated and scss style support, use below command.

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

This will create angular-ssr-example folder, where our angular code will be located. Let’s open that in VS Code or your favourite code editor.

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

We are going to create two new angular modules, one for the browser and one for the server, which import our app module generated by the CLI. These modules will generate their own distribution folders. Express server will use distribution folder of server module dist-server to inject appropriate HTML (based on request URL) inside app-root block of index.html of the browser distribution folder dist-browser and return that file. 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: Make application production ready

Since we have created angular application, we need to modify this application to make it production ready. 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

These will generate home and about components inside src/app/components folder. Then we need to import these components inside routing module which will be app-routing.module.ts inside src/app folder.

// app-routing.module.ts

import { Routes, RouterModule } from ' import { NgModule } from ' @angular/core ';import { Routes, RouterModule } from ' @angular/router '; import { AboutComponent } from './components/about/about.component';

import { HomeComponent } from './components/home/home.component'; const routes: Routes = [

{ path: '', component: HomeComponent },

{ path: 'about', component: AboutComponent },

{ path: '**', redirectTo: '/' },

];

imports: [RouterModule.forRoot(routes)],

exports: [RouterModule]

})

export class AppRoutingModule { } @NgModule ({imports: [RouterModule.forRoot(routes)],exports: [RouterModule]})export class AppRoutingModule { }

Since, we are implementing different components for different routes, we need to add a router-outlet inside app.component.html . Let’s also add some CSS styles to app component as well.

I guess we should also make our application pretty by setting a good in-built font inside styles.scss file.

(styles.scss)

You can add some functionality to home component and about component.

In home component, we have added a simple message and angular logo image. From home.component.ts, we have changed meta title and meta description of the page. Since, this is the landing view, we expect the page title for route / to be mentioned above.

Similarly in about component, we have changed the meta tags but we are importing users data from a remote server as well. This data will be dynamically injected inside ul element on the client side but we expect it to be available (already rendered) in the response from the server.

Since, we are done with our application at the moment, we can preview it by running command ng serve . This preview will be available at localhost server at 4200 port and will look like below.

Make sure you have HttpClientModule module imported in app.module.ts , else application won’t work.

Click on different buttons to see if views are changing, which should. You can also see meta-tags changing. But it you see the source using ctrl+u or view page source option in context menu of the chrome browser, you can only see index.html file with empty app-root .

At this point, we know that, this is what search engines and social media bots see when they visit our website. Since, component HTML gets injected inside <app-root></app-root> on client side dynamically, server side rendering simply means, inject it on server itself. To do that, we need to perform other key steps as below.

2. Create browser and server modules

We need to create two new angular modules manually which helps angular CLI to generate different build files.

We already have app.module.ts which was generated by angular CLI, modify in such a way that it looks like below.

( app.module.ts )

We basically removed bootstrap property, hence app module will no longer bootstrap our application.

Now, we are going to create browser.app.module.ts file with below content in the same directory of app.module.ts .

( browser.app.module.ts )

This module will be used by browser and will bootstrap the application on the client side. BrowserModule.withServerTransition({ appId: ‘ssr-example’ }) line imports BrowserModule with some configuration that tells angular that this is server side rendered app. appId file is name of your app which should be unique but you can set it to anything.

Similar, we also need one module for the server which would be server.app.module.ts in the same directory with below content.

( server.app.module.ts )

Server module looks exactly like browser module but we also need to import ServerModule from @angular/platform-server module. So, if you don’t have it, make sure to install it using below command.

npm install --save @angular/platform-server

3. Create separate entry points

Since, we are done with modules, we need different entry points for these modules. Normally, default entry point generated by CLI is app/main.ts but we need to separate entry points, one from browser and one for the server.

Hence rename main.ts to browser.main.ts and it should have below content.

( browser.main.ts )

Similar, entry point for server module will be located at server.main.ts in the same folder with below content.

( server.main.ts )

4. Create tsconfig.json files

We already have tsconfig.app.json file inside src folder. That is typescript configuration generated by CLI for app module. Since, we have two different modules for different roles, we need to have two different configuration.

Rename tsconfig.app.json to browser.config.app.json which will extend typescript configuration for browser module.

( browser.config.app.json )

Also create server.config.app.json file with below content which will extend typescript configuration for server module.

( server.config.app.json )

5. Modify ‘.angular-cli.json’ file

We have modified typescript config files to use different modules while building different distribution files. Now, we need to modify .angular-cli.json file to a add extra app entry for the server module. We also need to modify existing app entry to target renamed and new files.

We have two apps because there are two modules bootstrapping the application.

( .angular-cli.json )

If you see server specific app entry, we don’t have polyfills , styles , scripts and other entries. This is because, our server app references browser app which already contains those resources.

6. Create distributions

Until now, what we have done is instruct Angular CLI on how to create distribution files for browser app and server app. It’s time to create distributions (builds) for these apps. To simplify, modify your package.json file to include these commands under scripts .

"scripts": {

"build:browser": "ng build --prod --app 0",

"build:server": "ng build --prod --app 1 --output-hashing none",

"build": "npm run build:browser && npm run build:server",

"serve": "node server.js"

},

To build browser app, you need to use npm run build:browser and to build server app, you need to use npm run build:server . But once you are done with some update in application, you should run only npm run build .

After running npm run build , you should see dist-browser and dist-server folders in project root.

7. Create express server

It’s time to create an express server which will render application HTML on the server. Create server.js file inside project root with below content.

( server.js )

Make sure to install @nguniversal/express-engine from below command. This module is necessary to inject appropriate HTML inside app-root based on the route (request URL).

npm install --save @nguniversal/express-engine

Apart from angular specific code in server.js , other things should be pretty basic if you are a Node.js developer. To run the server, use below command.

npm run serve

This will start a HTTP server on port 3000 . Open your browser on http://localhost:3000 URL which should serve HTML rendered on the server.

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.

You can now use server.js with pm2 or forever.js to run in background forever.

8. State transfer

If you visit http://localhost:3000/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.