DEV Community

Cover image for Angular 17 Upgrade Guide with SSR
Mirza Leka
Mirza Leka

Posted on

Angular 17 Upgrade Guide with SSR

I was recently asked to upgrade the existing Angular app with a new Angular SSR framework. However, what I didn't know at the time is that Angular SSR only works on Angular 17. So I'm writing this guide that will teach you how to upgrade your existing Angular app to v17 as well as add server-side rendering.

Node.js Compatibility 🌐

Since Angular depends on Node.js, you need to have compatible versions of both. That’s why the Angular team has this compatibility table to let you know which version of Node.js is compatible with the version of Angular.


If you need to upgrade your Node.js version, be sure to go to and download the recommended version or use the NVM instead:

> nvm install 18 # Install Node.js v18

> nvm use 18 # Switch to installed version

# Now using node v18.x.x
Enter fullscreen mode Exit fullscreen mode

Since I was using Angular 16 with Node.js 18, I didn't need to change my Node.js version.

Ng Update 🕹️

The easiest way to upgrade to a newer version of Angular is to execute the ng update command in your CLI in the project folder.

Upon running the command you'll get tips on what to upgrade, e.g.

> ng update

You need to manually upgrade the following:
Angular CLI => ng update @angular/cli 
Angular Core => ng update @angular/core 
Angular CDK => ng update @angular/cdk
Enter fullscreen mode Exit fullscreen mode

Angular CLI

To get started, upgrade your current CLI version to the latest one:

> ng update @angular/cli 
Enter fullscreen mode Exit fullscreen mode

Upon updating the CLI you might be prompted to commit your changes to the Git before proceeding.

Angular Core

The next step is to upgrade the Angular framework, which you do using:

> ng update @angular/core 
Enter fullscreen mode Exit fullscreen mode

The core framework is now upgraded to v17 which you'll notice in the dependencies list in your package.json file.

Angular Material

Since I'm using Angular Material in my project I needed to upgrade it before continuing with the rest of the updates.

> ng update @angular/material 
Enter fullscreen mode Exit fullscreen mode

Angular CDK

With Material sorted out, I installed the last mandatory update (on the previous list) which was Angular CDK:

> ng update @angular/cdk
Enter fullscreen mode Exit fullscreen mode

Now the project should be fully migrated to the latest version.

For some reason, I also needed to fix the paths of all the imports of my custom components, services, models, etc, and TSConfig errors (which you can solve by copying the tsconfig from here), but this is a pretty small complaint.

Adding Angular SSR 🚀

The new Angular/SSR package replaces the Angular Universal. The new framework is faster and more intuitive.

> ng add @angular/ssr # installs Angular SSR
Enter fullscreen mode Exit fullscreen mode

For those of you coming from Next.js, Angular uses Express.js to handle the backend routing and CommonEngine (from Angular/SSR) for rendering an Angular app on the Express.js server.

Fixing Config Imports

Upon setting up the Angular SSR framework, Angular will create a server.ts (Express.js file that loads the app) and a main.server.ts file (that is equivalent to Angular App Module but for the server).
You may run into an issue with the current Angular config not knowing the correct path of the two newly added files.

Make sure to set this properties to current paths:


    "server": "src/main.server.ts", // path to your file
Enter fullscreen mode Exit fullscreen mode

  "files": [
    "src/main.ts", // path to your file
    "src/main.server.ts", // path to your file
Enter fullscreen mode Exit fullscreen mode

HTTP Client Configuration

Upon running the HTTP services with SSR, Angular might throw this error:
NG02801 is not an error but a warning that encourages the developers to enable the fetch implementation of the HttpClient.

To resolve it, you need to add provideHttpClient(withFetch()) to your app.config.ts file or app.module.ts (if you're using the old NgModules):

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration } from '@angular/platform-browser';
import { provideHttpClient, withFetch  } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()), // here it is!
Enter fullscreen mode Exit fullscreen mode

Connecting Frontend & Backend

As a proof of concept, create a dummy endpoint in the server.ts file:

  server.get('/api/greetings', (req: express.Request, res: express.Response) => {
    res.send({ message: 'Hello from Angular SSR!' });
Enter fullscreen mode Exit fullscreen mode

Generate a new Angular service using:

> ng g s services/greetings
Enter fullscreen mode Exit fullscreen mode

And connect to your API via HTTP Client:

import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

interface IGreetings {
  message: string

  providedIn: 'root'
export class GreetingService {

  // alternative to the class constructor
  private readonly httpClient = inject(HttpClient);

  getGreetings(): Observable<IGreetings> {
    return this.httpClient.get<IGreetings>('/api/greetings');
    // notice how you don't need to prefix your route with localhost:PORT 
    // because it's coming from the same domain
Enter fullscreen mode Exit fullscreen mode

Create a button element inside the App Component (or any other component):

<button (click)="sayHello()">Say Hello!</button>
Enter fullscreen mode Exit fullscreen mode

And register a handler for this button within the TypeScript file. This handler will invoke the service you've just created.

import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { GreetingService } from './services/greeting.service';

  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
export class AppComponent {

  private readonly greetingsService = inject(GreetingService);

  sayHello() {
Enter fullscreen mode Exit fullscreen mode

Running the App

Firstly build the app using the build script from the package.json file:

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "serve:ssr": "node dist/<your-app>/server/server.mjs"
Enter fullscreen mode Exit fullscreen mode
> ng build # or npm run build
Enter fullscreen mode Exit fullscreen mode

This will contain a dist folder in your main directory containing browser and server directories.

Then run the server and the client using the server:ssr script:

> npm run serve:ssr

> <your-app>y@0.0.0 serve:ssr
> node dist/<your-app>/server/server.mjs

Node Express server listening on http://localhost:4000
Enter fullscreen mode Exit fullscreen mode

If you then click the button on UI, you should see a message from the API printing in the console.


And that's it!

Why Angular 17 🎉

Angular 17 is a new iteration of the popular frontend framework made by Google. Version 17 ships with a host of exciting new features:

  • New control flow
  • Deferrable views
  • New Server-Side-Rendering framework
  • New Docs
  • Signal-based component inputs and two-way binding, just to name a few.

That's all from me today.
If you'd like to learn more about Angular, make sure to check out my other blog on Medium and follow me on Twitter to stay up to date with my content updates.

Bye for now 👋

Top comments (2)

jangelodev profile image
João Angelo

Hi Mirza Leka,
Your tips are very useful
Thanks for sharing

mirzaleka profile image
Mirza Leka

Thanks @jangelodev !