Angular Server Side Rendering For New Project
Step by step guide
Start from Angular 17, SSR was enabled by default when you create a new Angular project. To create a new Angular project, run the following command.
1 | ng new angular-ssr |
Press Y
on keyboard when prompted with the following question, then Angular will generate the boilerplate code for you.
1 | Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? (y/N) |
Compared with the traditional pure client-side rendering project, the Angular SSR project has the following differences:
Project structure changes for SSR
The following new files were added to the project:
- The
server.ts
file is added to the project root directory. - The
src/main.server.ts
file is added to the project source directory. - The
src/app/app.config.server.ts
file is added to the project source directory.
The following changes were made to the existing files:
- The
package.json
file has a newserver
script.1
2
3
4"scripts": {
// ...
"serve:ssr:angular-ssr": "node dist/angular-ssr/server/server.mjs" // <--- new
}, - The
angular.json
file has a newserver
configuration under thearchitect | build | options
section.1
2
3
4
5
6
7
8"options": {
// ...
"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "server.ts"
}
}, - The
tsconfig.app.json
filefile
section is updated to include thesrc/main.server.ts
file.1
2
3
4
5
6
7
8
9{
// ...
"files": [
"src/main.ts",
"src/main.server.ts", // <--- new
"server.ts" // <--- new
],
// ...
} - The
src/app/app.config.ts
file addprovideClientHydration
inproviders
.1
2
3export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes), provideClientHydration()]
};
Run app
Deployment
How to check if page is rendered by server or client
- Open the page in Chrome browser.
- Right-click on the page and select
View page source
. - Check the body tag, if the body tag is empty, it means the page is rendered in client side, the following page is a CSR page, since the body tag only contains
<app-root></app-root>
. that’s for the client side rendering.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html lang="en">
<head>
<script type="module" src="/@vite/client"></script>
<meta charset="utf-8">
<title>Angular SSR In Depth</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500& display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="styles.css"></head>
<body class="mat-typography">
<app-root></app-root>
<script src="polyfills.js" type="module"></script><script src="main.js" type="module">
Angular SSR for existing project
If you have an existing project and want to enable SSR, you can follow the steps below.
1 | ng add @angular/ssr |
This command will add the necessary files and configurations to your project to enable SSR.
Create
src/main.server.ts
file.1
2
3
4
5
6
7import { 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;Create
src/app/app.config.server.ts
file.1
2
3
4
5
6
7
8
9
10
11import { 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);Create
server.ts
file.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54import { 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',
index: 'index.html',
}));
// 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();Update
angular.json
file.Add the following in
architect | build | options
section.1
2
3
4
5"server": "src/main.server.ts",
"prerender": true,
"ssr": {
"entry": "server.ts"
}Update
tsconfig.app.json
file.Update
src/app/app.config.ts
file.Update
package.json
file.