DEV Community

Syed Muhammad Haris
Syed Muhammad Haris

Posted on

✨ Enabling SSR (Server-Side Rendering) in Angular Application

In this blog post, we’ll walk through how to enable Server-Side Rendering (SSR) in an Angular application. We’ll explore a real-world scenario and see step by step how SSR helps us solve it.


🧐 Why Add SSR?

Angular typically renders content in the browser, which isn’t optimal for search engines or initial load performance. SSR enables:

  • ✅ Search engine indexing of dynamic content
  • ✅ Fast first-paint and improved Time-to-Interactive (TTI)
  • ✅ Custom HTTP status codes like 404
  • ✅ Preview rendering for CMS editors

🔹 Scenario: Handling Custom HTTP Status Codes (e.g., 404)

Imagine you have different pages in your Angular app that need to return specific HTTP status codes.

For example, when a page is not found, you want the server to send back a proper ** 404** status code.

Normally, Angular applications running in the browser cannot send HTTP status codes. However, with SSR, we can inject the server’s response object and return custom status codes like 404 (Not Found).


🔧 Library Used

We use @nguniversal/express-engine, which provides an Express-based server adapter to render Angular on the server.


🔄 Step-by-Step Setup for SSR

Step 1: Add Angular Universal

Run the following command to add Angular Universal:

ng add @nguniversal/express-engine
Enter fullscreen mode Exit fullscreen mode

Step 2: Verify File Structure

After the command, ensure the following files exist:

  • server.ts
  • main.ts
  • main.server.ts
  • Updated angular.json with server target

📂 File Updates and Configuration

In server.ts

Make sure the server.ts file looks like this to support client-side routing:

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine, isMainModule } from '@angular/ssr/node';
import express from 'express';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import AppServerModule from './main.server';
import { RESPONSE } from '@nguniversal/express-engine/tokens';
import https from 'https';
import fs from 'fs';

const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');

const httpsOptions = {
  key: fs.readFileSync('./ssl-certificates/ssl.key'),
  cert: fs.readFileSync('./ssl-certificates/ssl.crt')
};

const app = express();
const commonEngine = new CommonEngine();

app.get(
  '**',
  express.static(browserDistFolder, {
    maxAge: '1y',
    index: 'index.html'
  }),
);

app.get('**', (req, res, next) => {
  const { protocol, originalUrl, baseUrl, headers } = req;

  commonEngine
    .render({
      bootstrap: AppServerModule,
      documentFilePath: indexHtml,
      url: `${protocol}://${headers.host}${originalUrl}`,
      publicPath: browserDistFolder,
      providers: [
        { provide: APP_BASE_HREF, useValue: baseUrl },
        { provide: RESPONSE, useValue: res }
      ],
    })
    .then((html) => res.send(html))
    .catch((err) => next(err));
});

if (isMainModule(import.meta.url)) {
  const port = process.env['PORT'] || 4200;
  https.createServer(httpsOptions, app).listen(port, () => {
    console.log(`🚀 Angular SSR server running at https://localhost:${port}`);
  });
}

export default app;
Enter fullscreen mode Exit fullscreen mode

In main.ts

/// <reference types="@angular/localize" />
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

In main.server.ts

export { AppServerModule as default } from './app/app.module.server';
Enter fullscreen mode Exit fullscreen mode

⚠️Note: By updating the code in the file mentioned above, you can enable SSR in your Angular application.


🌍 How to Run SSR ?

You need to add a server script in package.json. In some cases, it’s added by default, but if not, you’ll need to add it manually as shown below:

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build",
  "watch": "ng build --watch --configuration development",
  "test": "ng test",
  "serve:ssr:Demo_App": "node dist/demo-app/server/server.mjs"
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Replace "Demo_App" and "demo-app" with your actual project name.

Then, Run this command:

npm run serve:ssr:Demo_App
Enter fullscreen mode Exit fullscreen mode

🔍 Real-World Use Case

🔌 SSR 404 Page Example

Now we will change the Status code of our specific component which will be render when ever we get the response code 404 then it will update the status code of that page to 404.

To handle 404 responses in our Angular SSR app, we’ll follow these steps:

1. Create a 404 Page Component

  • Build a dedicated component that will be rendered whenever the backend returns a 404 response.

2. Use the Page Registry

3. Render the 404 Page Component

  • When the 404 component is triggered, it receives the response data.
  • At this point, we replace the existing logic with the code shown below to update the HTTP status code to 404.
import { RESPONSE } from '@nguniversal/express-engine/tokens';

constructor(
  private router: Router,
  @Optional() @Inject(RESPONSE) private response: any
) {}

ngOnInit(): void {
  if (this.response) {
    this.response.statusCode = 404;
    this.response.statusMessage = 'Page Not Found';
  }
}
Enter fullscreen mode Exit fullscreen mode

⚠️ Note: Using the above code, you can easily update the status code of any page or component in Angular.

4. Architecture Note

  • This flow is fully compatible with the BFF (Backend for Frontend) architecture I’m using.
  • However, you can also customize and update HTTP status codes in your own project based on different conditions or requirements.

🌟 Final Thoughts

Enabling SSR in Angular using Angular Universal gives you a huge SEO and performance boost while keeping all the client-side flexibility of Angular.

Use this setup to build production-ready, CMS-driven, and search engine–friendly Angular applications!

Top comments (0)