Blog>
Snippets

Setting up Angular Universal for Server-Side Rendering

Demonstrate the steps to install Angular Universal package and setting up the server-side rendering (SSR) in an Angular app, which will improve SEO as crawlers can pre-render the content.
// Step 1: Install the Angular Universal package
ng add @nguniversal/express-engine --clientProject YOUR_APP_NAME
This command installs the necessary Angular Universal and `express-engine` packages and updates your application. Replace `YOUR_APP_NAME` with the actual name of your Angular application.
// server.ts (The Node Express server implementation used for SSR)

// Imports to setup the Node server
import 'zone.js/dist/zone-node';
import * as express from 'express';
import { join } from 'path';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';

// Express server
const app = express();
const port = process.env.PORT || 4000;
const distFolder = join(process.cwd(), 'dist/YOUR_APP_NAME/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

// Serve static files
app.get('*.*', express.static(distFolder, {
  maxAge: '1y'
}));

// Use the Angular Universal engine
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModule,
}));

app.set('view engine', 'html');
app.set('views', distFolder);

// Server-side rendering for Angular routes
app.get('*', (req, res) => {
  res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});

// Start up the Node server
app.listen(port, () => {
  console.log(`Node Express server listening on http://localhost:${port}`);
});
This is the setup for our Express server which will be responsible for rendering our Angular application on the server. This server uses the `ngExpressEngine` to render the application.
// Step 3: Add a new entry point for the server application (server.ts)

// Modify your Angular application's angular.json

"architect": {
  // ...
  "build": {
    // Existing configurations
  },
  "server": {
    "builder": "@angular-devkit/build-angular:server",
    "options": {
      "outputPath": "dist/YOUR_APP_NAME/server",
      "main": "server.ts",
      "tsConfig": "tsconfig.server.json"
    },
    "configurations": {
      "production": {
        "fileReplacements": [
          {
            "replace": "src/environments/environment.ts",
            "with": "src/environments/environment.prod.ts"
          }
        ],
        "sourceMap": false,
        "optimization": true
      }
    }
  },
  // Other architect targets
}
This step involves modifying the `angular.json` file to add a new target for the server build. Replace `YOUR_APP_NAME` with the name of your application. Also, make sure you have the corresponding `server.ts`, `tsconfig.server.json`, and environment files in your project.
// Step 4: Create a new application root module for the server (app.server.module.ts)

// The AppServerModule

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}
Creating a new root module for the server (`AppServerModule`) which will import our regular `AppModule` and the `ServerModule` from `@angular/platform-server`. We bootstrap the same `AppComponent` as our main module.
// Step 5: Update main.server.ts for server entry point

// The main entry point for the server

export { AppServerModule } from './app/app.server.module';
export { renderModule, renderModuleFactory } from '@angular/platform-server';
We need to update or create `main.server.ts` to set up the main entry point for the server application. This entry point exports `AppServerModule` and other functions from `@angular/platform-server` that are required for server-side rendering.
// Step 6: Build the application for SSR

// Running the build commands for both client and server
ng build --prod
ng run YOUR_APP_NAME:server:production
We'll build the application for production, which includes building both the client-side application and the server-side application using the Angular CLI's build commands. Make sure `YOUR_APP_NAME` matches your application's name.
// Step 7: Serve your application with SSR

// Option A: Use a Node/Express server (see server.ts setup)

// Option B: Deploy to a cloud function or a serverless environment

// It is crucial to run both builds before deploying the SSR server so that the most recent client and server bundles are served.
The final step is to serve the app with SSR, either using a Node/Express server (as per the `server.ts` provided) or deploying to a compatible cloud function or serverless environment.