Server-side rendering in ASP.NET Core Angular (2024 version)

Pieterjan De Clippel
4 min readNov 29, 2023

This is an updated version of following walkthrough.

Create the project

Start by creating a new ASP.NET Core project. Choose the Web API template:

If you wish to use the Visual Studio publish wizard, try putting your project as close to the drive root as possible (Yes, you’re right, Visual Studio 2022 still doesn’t support the LongPathApi…)

Off course we choose to use .NET 8

Make sure you have the latest version of angular installed. Run a cmd as administrator, and issue:

npm install --global @angular/cli@latest
ng version
> 17.0.3

Now we’ll create the ClientApp by running the following command from the project folder:

ng new ClientApp --style scss --routing --ssr=false

We’re not yet adding SSR here.

.NET Setup

  1. Open the NuGet package manager and install MintPlayer.AspNetCore.Hsts . This is an improved version of the ASP.NET Core HSTS Middleware, which works correctly for SPA’s. This middleware uses the OnStarting callback, and effectively adds the header to the HTTP response when visiting any page in your angular application.
  2. Also install the MintPlayer.AspNetCore.SpaServices.Routing package into your project. This package contains the RouteBuilder to parse visited urls in your backend. The dependency project Prerendering contains the SpaPrerendering middleware.

Add the following Program class

The only thing missing now is the SpaPrerenderingService

This service is registered as a scoped service, so you can inject repositories, DbContext through the constructor.

Modify your csproj file. Best bet is to just copy below:

Angular setup

Edit the angular.json and add below snippet inside the projects:ClientApp:architect section:

As you can see we need to add a tsconfig.server.json and main.server.ts file

Now install the additional packages

npm i aspnet-prerendering
npm i @angular/platform-server

Create the following file:


Add some scripts to your package.json

Now the output folder is still disconfigured, and the configuration won’t work when DOTNET_ENVIRONMENT = Production. To fix this, open the angular.json file and change the outputPath from dist/client-app to just dist.

Modify the main.ts:

Add node to the Types

The result is here:


Since the .NET template was updated by Microsoft, you need to make additional changes before starting the deployment to your live webserver.

Html Minification

  • Open the NuGet package manager, and install WebMarkupMin.AspNetCore8
  • Update the Program.cs


All rendered HTML is minified


npm i @ngx-translate/http-loader

Now we can setup the TranslateModule in both the browser and server module

At last add the "resolveJsonModule": true to the tsconfig.json under compilerOptions

Add the message to app.component.html :

<h1>{{ 'greeting' | translate }}, {{ title }}</h1>
Load the TranslateModule
Provide the HttpClient
Change the language based on the lang queryparam

And put 2 links at the bottom of the app component to navigate

Supplying data

Let’s send an object from .NET to angular during SSR. This object can be one fetched from the database whilst on the server.

In the main.server.ts we passed the entire to a provider. We can now inject it and use it on the app component:

We need to update the DataFromServer interface

Now let’s show this information on the app component:


There are 3 possible scenarios you can implement here:

  1. Retrieve the data on the server, use OnSupplyData to pass to angular — During client-side-rendering send another HTTP request to retrieve the same data
  2. Retrieve the data on the server, use OnSupplyData to pass to angular — In angular during server-side-rendering put the data into the TransferState , and read it during client-side-rendering ← Focus on this
  3. Use the new client-hydration feature with HTTP-caching in angular. In this scenario, the app is rendered on the server, sends HTTP requests to the API, these requests and responses are stored in the TransferState, and when the HTTP client is calling the same urls from the browser, the data is read from the TransferState . I don’t think I can use this feature because my live environment crashes when angular sends ajax calls to my api during SSR

The result is hosted here


  1. Important!!!!: Please check the output window. Half of the information needed to solve occasional problems with SSR is logged in the Visual Studio Output window, so the Visual Studio Output window is the most interesting source to diagnose issues here. If you’re experiencing problems, don’t hesitate to write back, but include the contents of your Output window.
  2. In previous walkthroughs I’ve written out some checks you can perform to solve several issues