Blazor — Set Title On Server and Client
By: Steve Peirce
Tags: Dotnet, Csharp, Aspnetcore, Blazor, Dotnet Core

Use this technique for updating all meta tags too…

Adding custom Head component to set the Title and other elements

At the moment Blazor does not have any first party support for setting elements inside the <head> tags. For any customer facing site this is a must-have!

I’ve seen a few posts showing how to set the Title on the client, but none for the Server. Let’s address that now…

TL;DR

Code is here…

Let’s start with Client Side

Setting the title on the client is easy, we just need to call a small piece of JavaScript.

First create a new file, I’ve called mine Head.js

Creating new JavaScript file — Head.js

We’ll create a new object on the window to avoid any naming conflicts, it will have a single function; setTitle(title) :

window.jsInteropHead = {

setTitle: (title) => document.title = title

};

We then need to add this reference in the _Host.cshtml below the existing scripts:

<script src="_framework/blazor.webassembly.js"></script>

<script>navigator.serviceWorker.register('service-worker.js');</script>

<script src="_content/Blender.Web.Shared/Scripts/Head.js"></script>

Note: I am always rendering on the server so I have deleted Index.html — You may need to add the script there as well if you plan on running a standalone client.

Wrap it up in HeadViewModel

In preparation for the Server-Side I’m actually going to introduce a very simple MVVM style ViewModel and corresponding EventArgs.

HeadViewModel:

public class HeadViewModel

{

public event EventHandler<HeadViewModelChangedEventArgs> HeadViewModelChanged;



public string Title { get; set; }



public void RaiseHeadViewModelChanged()

{

HeadViewModelChanged?.Invoke(this, new HeadViewModelChangedEventArgs(this));

}

}

HeadViewModelChangedEventArgs:

public class HeadViewModelChangedEventArgs : EventArgs

{

public HeadViewModelChangedEventArgs(HeadViewModel headViewModel)

{

HeadViewModel = headViewModel;

}



public HeadViewModel HeadViewModel { get; }

}

We need to register this as a Singleton on the Client in Blazor.Web.Client/Program.cs:

builder.Services.AddSingleton<HeadViewModel>();

And register as Transient on the server in Blazor.Web.Server/Startup.cs :

services.AddScoped<HeadViewModel>();

Create the Head Component

Next create the Head compnent — this will make it easy for us to set the page title and any other elements in the <head> tags.

Create a new Head.razor component.

Head.razor in file structure

@using Microsoft.JSInterop

@inject IJSRuntime JsRuntime;

@inject HeadViewModel HeadViewModel



@code {

[Parameter]

public string Title { get; set; }



protected override void OnInitialized()

{

HeadViewModel.Title = Title;

HeadViewModel.RaiseHeadViewModelChanged();

}



protected override async Task OnAfterRenderAsync(bool firstRender)

{

await JsRuntime.InvokeVoidAsync("jsInteropHead.setTitle", HeadViewModel.Title);

}

}

As you can see, this component takes a single Parameter and does not render any UI.

It updates the Title property on the HeadViewModel and raises an event. This will be used shortly for setting the title on the Server.

In the OnAfterRenderAsync method we invoke our JavaScript function to set the title.

Let’s use the Head Component

Great, we’re almost done now.

In each of your pages you can now set the Title:

Blender.Web.Client/Pages/Counter.razor:

@page "/counter"



<Head Title="Counter" />

@* ... *@

Blender.Web.Client/Pages/FetchData.razor:

@page "/fetchdata"

@inject WeatherForecastService WeatherForecastService



<Head Title="Weather Forecast" />

@* ... *@

Blender.Web.Client/Pages/Index.razor:

@page "/"



<Head Title="Wecome to Blazor!!!" />



<h1>Hello, world!</h1>

@* ... *@

Note: You might need to add the reference to your _imports.razor file:

@using Blender.Web.Shared

Now run the Blender.Web.Server application and you will see that the title is set:

Showing the page titles set in the browser

Finishing up the Server

When inspecting the traffic we will see that the HTML from the server is not setting the title, this isn’t great for SEO.

Dev tools showing the HTML markup returned by server.

Let’s update our App.razor — this is the entry point to the application. This allows us to optionally pass in an Action<HeadViewModel and subscriber to the `HeadViewModelChanged` event.

@inject HeadViewModel HeadViewModel



<Router AppAssembly="@typeof(Program).Assembly">

<Found Context="routeData">

<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />

</Found>

<NotFound>

<LayoutView Layout="@typeof(MainLayout)">

<p>Sorry, there's nothing at this address.</p>

</LayoutView>

</NotFound>

</Router>



@code

{

[Parameter]

public Action<HeadViewModel> OnHeadViewModelUpdated { get; set; }



protected override void OnInitialized()

{

if (OnHeadViewModelUpdated != null)

{

HeadViewModel.HeadViewModelChanged += (sender, args) =>

{

OnHeadViewModelUpdated(HeadViewModel);

};

}

}

}

Next we need to update Blender.Web.Server/Pages/_Host.cshtml .

We are going to update the code at the top of the page to the following:

@{

Layout = null;



var title = "Blender";



Action<HeadViewModel> onHeadViewModelUpdated = hvm =>

{

title = hvm.Title;

};



var app = await Html.RenderComponentAsync<App>(RenderMode.Static, new {OnHeadViewModelUpdated = onHeadViewModelUpdated});

}

We have declared a new title variable. We define our Action here and pass it as a property to the App.

Note that we are now rendering the App in code — this is because we need to render it before we can set the title — if not the title will not be updated.

In the Head tags we can now set our title:

<head>

<meta charset="utf-8"/>

<meta name="viewport" content="width=device-width, initial-scale=1.0"/>

<title>@title</title>

<base href="~/"/>

<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css"/>

<link href="css/app.css" rel="stylesheet"/>

<link href="manifest.json" rel="manifest" />

<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />

</head>

And finally, update the html to use our @app variable.

<app>

@* <component type="typeof(App)" render-mode="Static"/> *@

@app

</app>

That’s it — now we should be setting the title on the server for each of our pages!

Voila — We are now setting the title on the server!

You can see an example of this change on my experimental project here!