How to Use Angular Universal for Server-Side Rendering

    Monday, July 1, 202411 min read173 views
    How to Use Angular Universal for Server-Side Rendering

    Introduction to Angular Universal and Server-Side Rendering

    Angular Universal is a powerful tool that allows you to perform server-side rendering (SSR) for Angular applications. SSR enhances the performance of your application by pre-rendering the HTML on the server, which is particularly beneficial for improving SEO and the initial load time of your application.

    In this guide, we'll walk through the process of setting up Angular Universal with routing in Angular 16, from scratch up until serving the app, and explain how SSR works under the hood.

    Benefits of Angular Universal for SSR

    • Improved Performance: Faster initial load time as the server sends the pre-rendered HTML.

    • SEO Optimisation: Search engines can easily crawl fully rendered pages.

    • Better User Experience: Reduced time to be interactive for users, especially on slower devices.

    Setting Up Angular Universal with Angular CLI

    Step 1: Create an Angular Application

    If you don’t already have an Angular application, create one using the Angular CLI:

    ng new angular-ssr-app
    cd angular-ssr-app

    Step 2: Add Angular Universal

    Add Angular Universal to your project by running the following command:

    ng add @nguniversal/express-engine

    This command configures your project for SSR with Angular Universal and Express. It creates a server.ts file and updates your project files accordingly.

    Adding Routing to Your Angular Application

    Step 1: Generate Components

    Generate some components to use with routing. For example, create home and about components:

    ng generate component home --module=app.module
    ng generate component about --module=app.module

    Step 2: Set Up Routing

    Update your app-routing.module.ts to define routes for the home and about components:

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { AboutComponent } from './about/about.component';
    
    
    const routes: Routes = [
     {
       path: '',
       component: HomeComponent,
     },
     {
       path: 'about',
       component: AboutComponent,
     },
    ];
    
    
    @NgModule({
     imports: [
       RouterModule.forRoot(routes, {
         initialNavigation: 'enabledBlocking',
       }),
     ],
     exports: [RouterModule],
    })
    export class AppRoutingModule {}

    Step 3: Update Your App Component

    Update your app.component.html to include links to the routes and a router outlet:

    <nav>
     <a routerLink="/">Home</a>
     <a routerLink="/about">About</a>
    </nav>
    
    
    <router-outlet></router-outlet>

    app.component.scss:

    /* Styles for the navigation bar */
    nav {
     background-color: #2c3e50;
     padding: 10px;
     text-align: center;
    }
    
    /* Styles for the navigation links */
    nav a {
     color: #ecf0f1;
     text-decoration: none;
     font-size: 1.2em;
     margin: 0 15px;
     padding: 5px 10px;
     border-radius: 5px;
     transition: background-color 0.3s ease;
    }
    
    /* Hover effect for navigation links */
    nav a:hover {
     background-color: #34495e;
    }

    Step 4: Create the Home and About Components

    Home Component

    home.component.ts:

    import { Component } from '@angular/core';
    
    
    @Component({
     selector: 'app-home',
     templateUrl: './home.component.html',
     styleUrls: ['./home.component.scss'],
    })
    export class HomeComponent {}

    home.component.html:

    <div class="home-container">
     <h1 class="home-title">Welcome to the Home Page!</h1>
     <p class="home-content">
       This is the home page of the Angular Universal  application.
     </p>
    </div>

    home.component.scss:

    /* Add any specific styles for the home component here */
    .home-container {
     text-align: center;
     padding: 20px;
    }
    
    
    .home-title {
     font-size: 2.5em;
     color: #2c3e50;
     margin-bottom: 20px;
    }
    .home-content {
     font-size: 1.2em;
     color: #34495e;
    }

    About Component

    about.component.ts:

    import { Component } from '@angular/core';
    
    
    @Component({
     selector: 'app-about',
     templateUrl: './about.component.html',
     styleUrls: ['./about.component.scss'],
    })
    export class AboutComponent {}

    about.component.html:

    <div class="about-container">
     <h1 class="about-title">About Us</h1>
     <p class="about-content">
       This is the about page of the Angular Universal application.
     </p>
    </div>

    about.component.scss:

    /* Add any specific styles for the about component here */
    .about-container {
     text-align: center;
     padding: 20px;
    }
    
    .about-title {
     font-size: 2.5em;
     color: #27ae60;
     margin-bottom: 20px;
    }
    
    .about-content {
     font-size: 1.2em;
     color: #2ecc71;
    }

    Step 5: Update Server Configuration

    Ensure that your server.ts file correctly handles requests for all routes. The initial setup should cover this, but let's go through it for clarity.

    import 'zone.js/node';
    
    
    import { APP_BASE_HREF } from '@angular/common';
    import { ngExpressEngine } from '@nguniversal/express-engine';
    import * as express from 'express';
    import { existsSync } from 'node:fs';
    import { join } from 'node:path';
    import { AppServerModule } 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 distFolder = join(process.cwd(), 'dist/angular-ssr-app/browser');
     const indexHtml = existsSync(join(distFolder, 'index.original.html'))
       ? 'index.original.html'
       : 'index';
    
    
     // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/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';
    Employ Excellent Angular Development Services
    Angular Minds is a promising
    Angular web development company that has more than 13 years of experience in building scalable projects.

    Building and Running the Application

    Step 1: Build the Application

    First, build the client-side app, and the server-side render of parts of the client application with:

    npm run build:ssr

    This command will create two folders: dist/angular-ssr-app/browser and dist/angular-ssr-app/server.

    Step 2: Serve the Application

    To serve the application using Angular Universal:

    npm run serve:ssr

    You should see the output indicating a few new scripts that the server is running:

    Node Express server listening on http://localhost:4000

    Open your browser and navigate to http://localhost:4000 to see your server-rendered Angular application with routing.

    Example Walkthrough

    Home Page (/):

    • User navigates to http://localhost:4000/.

    • The server receives the request and renders the HomeComponent.

    • The server sends back fully rendered HTML for the HomeComponent.

    • A client receives and displays the HTML immediately.

    • Angular initializes on the client side and takes over for interactive elements.

    About Page (/about):

    • User navigates to http://localhost:4000/about.

    • Server receives the request and renders the AboutComponent.

    • Server sends back fully rendered HTML for the AboutComponent.

    • Client receives and displays the HTML immediately.

    • Angular initialises on the client side and takes over for interactive elements.

    How SSR Works

    Understanding SSR in Angular Universal

    Server-side rendering in Angular Universal works by pre-rendering the Angular application on the server before sending it to the client. Here’s a step-by-step breakdown of the whole angular server side rendering to client side rendering process:

    1. Initial Request: When a user makes an initial request to the server, the server uses Angular Universal to bootstrap the Angular application.

    2. Pre-rendering: The Angular application runs on the server, rendering the components and generating the HTML content.

    3. Sending HTML: The server sends the fully rendered HTML to the client's browser.

    4. Client-Side Bootstrapping: Once the HTML is loaded, Angular bootstraps on the client side, taking over from the static HTML and making the application interactive.

    This process ensures that the user sees a fully rendered page almost immediately, improving both search engine performance and SEO.

    Real-World Example

    Let's consider a real-world example to showcase the benefits of SSR. Imagine you have an e-commerce website built with Angular.

    Scenario Without SSR

    • When a user visits your homepage, their browser downloads the JavaScript bundles.

    • The Angular application runs in the browser and fetches data from the server.

    • The page renders after all these steps, which could take a few seconds on a slow connection.

    Scenario With SSR

    • When a user visits your homepage, the server pre-renders the Angular application.

    • The user immediately sees the fully rendered HTML content.

    • Angular bootstraps in the background, making the page interactive without the user noticing any delay.

    Benefits of This Scenario

    • Faster Load Time: Users see the content immediately, reducing bounce rates.

    • Better SEO: Search engines can easily crawl and index the pre-rendered HTML.

    • Improved Performance: Especially on mobile devices and for users with slower internet connections.

    • Improved User Experience: Users get a faster time to first paint and interactivity, leading to higher engagement and satisfaction.

    Advanced Topics in Angular Universal

    Fine-Grained Universal Application Shell

    • Implement a fine-grained Universal Application Shell to render content on the server selectively

    • This improves performance by only rendering what is necessary

    Integrating Angular Universal with ASP.NET Core

    • Angular Universal has also been integrated with ASP.NET Core (where it invokes a small Node process to manage the server-side rendering, piping all of the data back to ASP.NET and rendering it)

    Resources for Further Learning on Angular Universal and SSR

    • Visit Angular Tutorials to learn fundamental and advanced Angular knowledge from our Angular experts

    • Check out the Trilon Blog for more articles on Angular, NestJS, and much more!

    Conclusion and Next Steps

    In this tutorial, we've covered how to set up Angular Universal for server-side rendering (SSR) with routing in your Angular application. SSR offers significant benefits such as improved performance, SEO, and user experience by pre-rendering your application on the server.

    We walked through the steps of adding Angular Universal to your application code in the app root of Angular app in your project, setting up routing, creating components, and configuring the server. Additionally, we discussed how SSR works under the hood and provided a real-world example to illustrate its advantages.

    By implementing Angular Universal with SSR, you've taken a crucial step towards optimizing your Angular application for better performance and SEO. Feel free to explore customization options, optimize your application for web development and production, and learn about how Angular Universal works to leverage its full potential.

    If you have any questions or feedback, please leave a comment below. Happy coding with Angular Universal!

    24

    Related articles

    This website uses cookies to analyze website traffic and optimize your website experience. By continuing, you agree to our use of cookies as described in our Privacy Policy.