0%

angular-ssr

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:

  1. The server.ts file is added to the project root directory.
  2. The src/main.server.ts file is added to the project source directory.
  3. The src/app/app.config.server.ts file is added to the project source directory.

The following changes were made to the existing files:

  1. The package.json file has a new server script.
    1
    2
    3
    4
    "scripts": {
    // ...
    "serve:ssr:angular-ssr": "node dist/angular-ssr/server/server.mjs" // <--- new
    },
  2. The angular.json file has a new server configuration under the architect | build | options section.
    1
    2
    3
    4
    5
    6
    7
    8
    "options": {
    // ...
    "server": "src/main.server.ts",
    "prerender": true,
    "ssr": {
    "entry": "server.ts"
    }
    },
  3. The tsconfig.app.json file file section is updated to include the src/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
    ],
    // ...
    }
  4. The src/app/app.config.ts file add provideClientHydration in providers.
    1
    2
    3
    export const appConfig: ApplicationConfig = {
    providers: [provideRouter(routes), provideClientHydration()]
    };

Run app

Deployment

How to check if page is rendered by server or client

  1. Open the page in Chrome browser.
  2. Right-click on the page and select View page source.
  3. 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
    <!doctype html>
    <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&amp; 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"></ script></body>
    </html>

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.

  1. Create src/main.server.ts file.

    1
    2
    3
    4
    5
    6
    7
    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;
  2. Create src/app/app.config.server.ts file.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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);
  3. 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
    54
    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',
    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();
  4. 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"
    }
  5. Update tsconfig.app.json file.

  6. Update src/app/app.config.ts file.

  7. Update package.json file.

References

  1. https://www.youtube.com/watch?v=U1MP4uCuUVI