Server Side rendering with Angular 18
The more well-known a website is, the higher its number of visitors will be.
Whether it is known or not will depend on its SEO .
If your site is written with javascript , SEO will not work.
To solve this problem you must use the concept of Server Side Rendering or SSR
In this tutorial we will create a web application with Angular version 18.0.2
Then we will apply the Server Side Rendering in this Web Application.
How to do it ?
To start our project here is a summary of what we are going to do.
- SEO and SSR Theory
First of all we need to understand what SEO is.
SEO, SEO and SSR, everything is explained in detail
- Initializing the project from scratch
We will use Angular CLI for setting up a project,
Using best practices we will deploy the SSR. - Using a complex project
We will use a complete project that we will adapt to use the SSR. - Project update
Check used dependencies and update them. - Deployment
How to deploy and test our application on the internet.
- Source code
The complete project code is available on Github.
Theory
What is the Internet?
- Billions of humans
- Millions of websites on a spider's web
- And above all the way to bring them together
Let's start with humans
There are billions of them, they lead their lives and ask themselves billions of questions every day.
The Internet is the well-known www .
Or World Wide Web literally the global spider web .
There are millions of websites that are supposed to answer all your questions.
So it boils down to that.
Questions on one side, answers on the other.
We can already imagine supply and demand
To bring all these beautiful people together we will need intermediaries.
These will be the search engines or Search engine (SE in SEO!)
There are a lot of search engines.
But if we make a classification it gives this list
- Google: 91.88%
- Bing: 3.19%
- Yandex: 1.52%
- Yahoo! : 1.33%
- Baidu: 0.76%
- DuckDuckGo: 0.64%
This is called a hegemony.
So goes the world we will adapt.
In any case the summary for our offer and our request will be this image.
You would have understood it.
A well-known website is a site that has good SEO.
And above all who has good SEO with Google. CQFD and no choice.
Javascript and SEO
If you are a developer or want to become one, you will need to use tools to build a website.
Currently 3 tools stand out for creating a site.
- Angular
- React
- Vuejs
These are tools that use Javascript language.
And the problem is that Javascript is the problem.
If you are using Javascript the google SEO will not work.
I will explain later in this tutorial why in detail.
So we have to find a solution.
In the case of Angular the solution is called Server Side Rendering or SSR.
And we will use a dedicated tool for this Angular Universal.
Now let's get down to business.
What are we going to do ?
I offer you two possibilities
- Creating an SSR project from scratch
- Using an existing project and adding the SSR
Let's start with the quickest but least detailed solution.
Creating a From Scratch application
If you are in a hurry, go further.
I give you the commands that allow you to obtain an SSR application in a few minutes
I admit that it's a little brief but it has the merit of working (and rather well in fact).
If you want more explanations on what was done I advise you to move on and use an existing project.
# Uninstall Angular CLI (in case an old version of Angular was installed)
npm uninstall -g @angular/cli
# Install Angular CLI specific version (latest if possible)
npm install -g @angular/cli@18.0.3
# Create a demo directory (the name here is arbitrary)
mkdir demo
# Go to this directory
cd demo
# Generate a project called angular-starter with manual choice of options (answer yes to everything)
ng new angular-starter
# Position yourself in the project
cd angular-starter
# Run the application in normal mode
npm run start
# Test the app in your browser
http://localhost:4200
# Installing the necessary dependencies for SSR
ng add @angular/ssr
# Build the application in SSR mode
npm run build
# Run the application in SSR mode
npm run serve:ssr:angular-starter
# Test the app in your browser
http://localhost:4000
What are we going to do ?
I offer you the second possibility for the most curious and the most patient.
The base Angular project we will use already has the following features
- Generated with Angular CLI
- Routing
- Lazy Loading
- The Bootstrap CSS framework
All sources used are indicated at the end of the tutorial.
The final application is at the following address
Before you start
To be visited by a large number of users, a website must meet two essential conditions.
- Show up as quickly as possible.
- Be well referenced by search engines.
The technique that allows you to do this has a name.
- Server Side Rendering in English.
We will apply this technique in an Angular project.
For this we will use the technology recommended by the Google teams
- Angular Universal.
This technology will improve the natural referencing or SEO ( Search Engine Optimization ) of our site.
Creating the Angular project
To be able to continue this tutorial we must obviously have certain elements
- Node.js : The javascript platform
- Git : The version control software.
- Angular CLI : The tool provided by Angular.
- Visual Studio code : A code editor.
You can consult the following tutorial which explains in detail how to do it.
We will use an existing project
# Create a demo directory (the name here is arbitrary)
mkdir demo
# Go to this directory
cd demo
# Get the source code on your workstation
git clone https://github.com/ganatan/angular-modules.git
# Go to the directory that was created
cd angular-modules
# Run the installation of dependencies (or libraries)
npm install
# Run the program
npm run start
# Check how it works by running the command in your browser
http://localhost:4200/
Theory
Web pages generated with javascript frameworks use javascript.
Search engines currently have difficulty interpreting javascript.
We will verify this notion in a practical way.
We will run our application with the corresponding script.
# Running the application
npm run start
# Displaying the site in the browser
http://localhost:4200/
We will check the source code produced in the corresponding page.
Using the Chrome browser you have to type Ctrl + U to see the html code.
We notice that the “ Features ” code which is displayed in the browser does not appear in the code.
<!doctype html>
<html lang="en">
<head>
<script type="module" src="/@vite/client"></script>
<meta charset="utf-8">
<title>AngularStarter</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- Google tag (gtag.js) -->
<script async="" src="https://www.googletagmanager.com/gtag/js?id=YOUR-ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'YOUR-ID');
</script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<app-root></app-root>
<script src="polyfills.js" type="module"></script>
<script src="scripts.js" defer></script>
<script src="main.js" type="module"></script>
</body>
</html>
By clicking on main.js we open this file which contains the text "Features"
Let's run a build with npm run build.
The dist/angular-starter directory contains the main.js file .
Let's open this file with our VS code editor, then do a search (Ctrl + F) for the text "Features".
It contains the text "Features".
The main.js file is a javascript file, so it will be misinterpreted by search engines.
We will see later in this tutorial that once SSR has been applied, the code appears directly in the HTML code and will thus be well interpreted by search engines.
Installation
The tool we will use to apply SSR to our project is
- @angular/ssr version 18.0.3
The previous tool used until Angular version 16 was
- Angular universal version 16.2.0
The latest version of this tool is available below
@angular/ssr allows generating static pages through a process called Server side rendering (SSR) .
The procedure to follow is detailed on the official Angular website.
https://angular.io/guide/ssr
We will use a simple CLI command
# Installation
ng add @angular/ssr
Angular universal
As a reminder, Angular CLI uses the principle of schematics via the ng add directive to modify our code and adapt it to the new functionality (here the ssr).
Many operations were performed automatically on our project.
If we had to carry out this operation manually here are the different steps that we would have had to follow.
- Installing the new necessary dependencies
- Editing the angular.json file
- Creating the src/app/ app.config.server.ts file
- Creating the src/ main.server.ts file
- Creating the server.ts file
- Editing the tsconfig.app.json file
- Editing the src/app/ app.config.ts file
- Editing the package.json file
Installation des dépendances.
# Install new dependencies in package.json
npm install --save @angular/platform-server
npm install --save @angular/ssr
npm install --save express
npm install --save @types/express
npm install --save @types/node
Editing the angular.json file.
Adding SSR-related properties
- server
- prerender
- ssr
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/angular-starter",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"node_modules/@fortawesome/fontawesome-free/css/all.min.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css",
"src/assets/params/css/fonts.googleapis.min.css",
"src/styles.css"
],
"scripts": [
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"
],
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "server.ts"
}
},
Creating the app.config.server.ts file
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering()
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
Creating the main.server.ts file
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';
const bootstrap = () => bootstrapApplication(AppComponent, config);
export default bootstrap;
Creating the server.ts file
The port used by default is 4000 we can change it if necessary in this file.
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { dirname, join, resolve } from 'node:path';
import bootstrap 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 serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const commonEngine = new CommonEngine();
server.set('view engine', 'html');
server.set('views', browserDistFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserDistFolder, {
maxAge: '1y'
}));
// All regular routes use the Angular engine
server.get('*', (req, res, next) => {
const { protocol, originalUrl, baseUrl, headers } = req;
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: `${protocol}://${headers.host}${originalUrl}`,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
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}`);
});
}
run();
Editing the file
- tsconfig.app.json
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": [
"node"
]
},
"files": [
"src/main.ts",
"src/main.server.ts",
"server.ts"
],
"include": [
"src/**/*.d.ts"
]
}
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideClientHydration()]
};
Editing the package.json file
"scripts": {
...
"serve:ssr:angular-starter": "node dist/angular-starter/server/server.mjs"
...
}
Update
We can take advantage of this to update the dependencies of the package.json file and adapt the version descriptors.
The following dependencies
- @nguniversal/express-engine
Can be updated with versions
- 18.0.3
The file will ultimately contain the following dependencies.
"dependencies": {
"@angular/animations": "18.0.2",
"@angular/common": "18.0.2",
"@angular/compiler": "18.0.2",
"@angular/core": "18.0.2",
"@angular/forms": "18.0.2",
"@angular/platform-browser": "18.0.2",
"@angular/platform-browser-dynamic": "18.0.2",
"@angular/platform-server": "18.0.2",
"@angular/router": "18.0.2",
"@angular/ssr": "18.0.3",
"@fortawesome/fontawesome-free": "6.5.2",
"bootstrap": "5.3.3",
"express": "4.19.2",
"rxjs": "7.8.1",
"tslib": "2.6.3",
"zone.js": "0.14.7"
},
"devDependencies": {
"@angular-devkit/build-angular": "18.0.3",
"@angular/cli": "18.0.3",
"@angular/compiler-cli": "18.0.2",
"@types/express": "4.17.21",
"@types/jasmine": "5.1.4",
"@types/node": "20.14.2",
"angular-eslint": "18.0.1",
"eslint": "9.4.0",
"jasmine-core": "5.1.2",
"karma": "6.4.3",
"karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"typescript": "5.4.5",
"typescript-eslint": "8.0.0-alpha.20"
}
Debuggage
The application note code will cause an error during compilation.
So the code before after.
It concerns the app.component.ts file
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink, RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterLink, RouterOutlet],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'angular-routing';
footerUrl = 'https://www.ganatan.com';
footerLink = 'www.ganatan.com';
ngOnInit(): void {
const navMain = document.getElementById('navbarCollapse');
if (navMain) {
navMain.onclick = function onClick() {
if (navMain) {
navMain.classList.remove("show");
}
}
}
}
}
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink, RouterOutlet } from '@angular/router';
import { Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterLink, RouterOutlet],
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'angular-routing';
footerUrl = 'https://www.ganatan.com';
footerLink = 'www.ganatan.com';
constructor(
@Inject(PLATFORM_ID) private platformId: object) {
}
ngOnInit(): void {
if (isPlatformBrowser(this.platformId)) {
const navMain = document.getElementById('navbarCollapse');
if (navMain) {
navMain.onclick = function onClick() {
if (navMain) {
navMain.classList.remove("show");
}
}
}
}
}
}
Conclusion
All that remains is to test all the previous scripts and finalize with the SSR.
# Development
npm run start
http://localhost:4200/
# Tests
npm run test
# AOT Compilation
npm run build
# SSR Compilation
npm run build:ssr
npm run serve:ssr
http://localhost:4000/
Enfin nous allons vérifier le code source produit dans la page correspondante à la compilation SSR..
En utilsant le navigateur Chrome il faut taper Ctrl + U pour voir le code html.
On remarque que le code "Features" s'affiche cette fois dans le navigateur.
La page sera dès lors bien interprétée par les moteurs de recherche.
Noticed
Some versions of Angular 9 do not allow you to check the SSR result in your browser.
However SSR works on the server side with Google robots.
To check it use the curl software
- For example on localhost
curl http://localhost:4000/ > ssr-results.txt
- On the Demo version
curl https://angular.ganatan.com/ > ssr-results.txt
Then check the contents of the ssr-results.txt file
You will see the desired text appear in the HTML code.
Other proof Use SEOQUAKE (SEO Toolbox) to check SEO on angular.ganatan.com/
I apply SSR on www.ganatan.com and angular.ganatan.com
Code source
The source code used at the start of the tutorial is available on github
https://github.com/ganatan/angular-react-modules
The source code for this tutorial is available on GitHub.
Use git to grab this code and check how it works.
Simply go to the following address
https://github.com/ganatan/angular-ssr
And don't forget the P'tits Loups if you like the source code you know what you have to do.
A star on github can change a man
The following steps will allow you to obtain a prototype application
- Step 7: Progressive Web App with Angular
- Step 8: Search Engine Optimization with Angular
- Step 9: HttpClient with Angular
The last step provides an example application
The source code of this final application is available on GitHub
https://github.com/ganatan/angular-app