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

Pieterjan De Clippel
4 min readMay 22, 2020

Server-side rendering in ASP.NET Core Angular

Start by creating a new ASP.NET Core project. Choose the Angular template

You can already run the application. But when you look at the source code, you’ll see that there’s pretty much nothing on the page. The browser of the visitor renders the app. This is not good when SEO is important.

There is no content in the app-root tag

How do I add SSR

Go to the Solution Explorer and right-click the project file. Choose Edit project file from the context menu and set BuildServerSideRenderer to true:

Setting this flag will cause ASP.NET Core to trigger the ng build and ng build:ssr commands. When these commands succeed, a browser and server folder is generated in the ClientApp/dist folder.

Next, remove the ClientApp folder from the project and create a new project:

cd SpaPrerendering
ng new ClientApp --style=scss --routing

Add the express engine of Angular Universal to the project:

cd ClientApp
ng add @nguniversal/express-engine --clientProject ClientApp

This command generates the AppServerModule, main.server, tsconfig.server.json and tsconfig.app.json in the project.

Install the aspnet-prerendering node package:

npm install --save aspnet-prerendering

Add the SPA prerendering middleware. I removed the unnecessary parts of the code:

Select all 4 tsconfig files in the ClientApp folder, go to the properties window and modify the Build Action to Content.

The tsconfig files are setup correctly, so there’s nothing to change here right now.

However, in the latest version of the schematic we just used (@nguniversal/express-engine version 9.1.1) there’s a typo. Open the angular.json file and modify the following node: projects:ClientApp:architect:server:options:main. The value for this property is probably server.ts. Change this value to src/main.server.ts.

In case you’re using providers, define them in the main.ts (ClientApp/src/main.ts):

Also modify the main.server.ts: (ClientApp/src/main.server.ts):

Now when you’d try to run the project, you’ll probably get the following error:

NodeInvocationException: Prerendering failed because of error: Error: Cannot find module SpaPrerendering\ClientApp\dist\server\main.js

This is because the app already starts to run before the Angular CLI server has finished building the app server-side. The ClientApp/dist/server folder simply doesn’t exist yet. Wait until the folder exists and run the project again.

(For some reason you may have to restart Visual Studio in order to get this step working).

And when we run again…

The final project is available on GitHub

Pass data from .NET to the Angular CLI during SSR

When you want to render a page that for example displays the information of a person, you’ll have to pass the person from ASP.NET to the angular app during the Server-side prerendering process.

Start by editing the Startup file:

Add the SupplyData command

Modify the main.ts. Provide some message value:

Modify the main.server.ts. Pass the value from the server to the provider:

Inject the message-provider in the AppComponent:

Now you can display the message on the AppComponent’s view:

You can pass any serializable object (like a person) to the SupplyData delegate, and use this object in the component’s view.

Result received from the server
Result after the browser loaded the Angular app

Commit reference

Pass data based on the activated url

I’ve created a NuGet package that lets you define your SPA-routes in C#, in order to have them available from ASP.NET Core: MintPlayer.AspNetCore.SpaServices.Routing

  1. Define your SPA routes using the SpaRouteBuilder
  2. Inject the ISpaRouteService in the Configure method of your Startup
  3. Get the activated route on page load: `var route = spaRouteService.GetCurrentRoute(context)`
  4. Get the parameters from the route: `route.Parameters[“id”]`
  5. Generate urls by route name: `spaRouteService.GenerateUrl(“person-show-name”, new { id = id, name = person.Text.Slugify() })`

For a real-life example: https://github.com/MintPlayer/MintPlayer/blob/master/MP.Web/Startup.cs

Handling translations during SSR

This part is coming soon. If you’re curious about this:

Pass data from server-side rendered to the client app without sending a webrequest

Provide just someBACKGROUND_OPACITY value (any value)

Retrieve the value you need (BackgroundOpacity) from anywhere it’s stored, and pass it through the data array

Provide the BACKGROUND_OPACITY you passed, on the server-side

Import the BrowserTransferStateModule in the AppModule

Import the ServerTransferStateModule in the AppServerModule

Now it’s just a matter to register the value during server-side rendering, and reading the value during client-side rendering

--

--