Server-side rendering (SSR) with Angular Universal
Balaji
Posted on February 23, 2022
Angular Server-side rendering(SSR) with Angular Universal
Angular Universal, a technology that renders Angular applications on the server.
In this post we will walk through Server-side rendering(SSR) with Angular Universal step by step.
Installation
Use the package manager universal to install universal.
ng add @nguniversal/express-engine
Updated Filles after installation
src/
index.html app web page
main.ts bootstrapper for client app
main.server.ts * bootstrapper for server app
style.css styles for the app
app/ ... application code
app.server.module.ts * server-side application module
server.ts * express web server
tsconfig.json TypeScript base configuration
tsconfig.app.json TypeScript browser application configuration
tsconfig.server.json TypeScript server application configuration
tsconfig.spec.json TypeScript tests configuration
angular.json
"outputPath": "dist/angular-poc",
// Replace below Command in place of above one
"outputPath": "dist/angular-poc/browser",
Include below configuration into angular.json file
{
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/angular-poc/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json",
"inlineStyleLanguage": "scss"
},
"configurations": {
"production": {
"outputHashing": "media",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"optimization": false,
"sourceMap": true,
"extractLicenses": false
}
},
"defaultConfiguration": "production"
},
"serve-ssr": {
"builder": "@nguniversal/builders:ssr-dev-server",
"configurations": {
"development": {
"browserTarget": "angular-poc:build:development",
"serverTarget": "angular-poc:server:development"
},
"production": {
"browserTarget": "angular-poc:build:production",
"serverTarget": "angular-poc:server:production"
}
},
"defaultConfiguration": "development"
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"routes": ["/"]
},
"configurations": {
"production": {
"browserTarget": "angular-poc:build:production",
"serverTarget": "angular-poc:server:production"
},
"development": {
"browserTarget": "angular-poc:build:development",
"serverTarget": "angular-poc:server:development"
}
},
"defaultConfiguration": "production"
}
}
src/app/app.module.ts
Add below code instead of BrowserModule
BrowserModule.withServerTransition({ appId: "serverApp" });
src/app/app.server.module.ts
Create a new file inside src/app folder
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 {}
src/main.server.ts
Create a new file inside the project directory
/***************************************************************************************************
* Initialize the server environment - for example, adding DOM built-in types to the global scope.
*
* NOTE:
* This import must come before any imports (direct or transitive) that rely on DOM built-ins being
* available, such as `@angular/elements`.
*/
import "@angular/platform-server/init";
import { enableProdMode } from "@angular/core";
import { environment } from "./environments/environment";
if (environment.production) {
enableProdMode();
}
export { AppServerModule } from "./app/app.server.module";
export { renderModule, renderModuleFactory } from "@angular/platform-server";
src/main.ts
Add below code instead of platformBrowserDynamic().bootstrapModule(AppModule)
document.addEventListener("DOMContentLoaded", () => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
});
tsconfig.server.json
Create a new file inside main directory
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/server",
"target": "es2019",
"types": ["node"]
},
"files": ["src/main.server.ts", "server.ts"],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
server.js
Create a new file inside project directory
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
const server = express();
const distFolder = join(process.cwd(), 'dist/angular-poc/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
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}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
package.json
Use dev:ssr
to run app locally
Use build:ssr
to generate the production build
"scripts": {
"dev:ssr": "ng run angular-poc:serve-ssr",
"serve:ssr": "node dist/angular-poc/server/main.js",
"build:ssr": "ng build && ng run angular-poc:server",
"prerender": "ng run angular-poc:prerender"
}
"dependencies": {
"@angular/platform-server": "~12.2.0",
"@nguniversal/express-engine": "^12.1.0",
"express": "^4.15.2"
}
"devDependencies": {
"@nguniversal/builders": "^12.1.0",
"@types/express": "^4.17.0"
}
Usage
npm run dev:ssr
Conclusion
In this article, you learned how to setup Server side rendering with Angular Application.
You can modify the configuration as per your needs.
Github source-code:- https://github.com/balajipatnam/angular/tree/main/ssr
Suggestions are welcome to improve this package.
Posted on February 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.