This is in reference to #10450.
Goal: Set meta tags (title, description etc) for SEO and Open Graph purposes with data coming from the page itself. Using the Javascript interop won’t help as pages won’t be able to be crawled.
I have used a suggestion by @honkmother and moved the base component further up the tree to encapsulate the <html>
tag but for some reason this has affected routing. All links are prepended with ~/
and I can’t seem to understand why.
I have created an example repo here if anyone is intersted in taking a look.
_Hosts.cshtml
@page "/"
@namespace BlazorMetaTags.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = null;
}
<!DOCTYPE html>
<component type="typeof(AppBase)" render-mode="ServerPrerendered" />
AppBase.cs
using BlazorMetaTags.Shared;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
namespace BlazorMetaTags
{
public class AppBase : ComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "html");
builder.AddAttribute(1, "lang", "en");
builder.OpenElement(2, "head");
builder.OpenComponent<Head>(3);
builder.CloseComponent();
builder.CloseElement();
builder.OpenElement(3, "body");
builder.OpenElement(4, "app");
builder.OpenComponent<App>(5);
builder.CloseComponent();
builder.CloseElement();
builder.OpenComponent<Body>(6);
builder.CloseComponent();
builder.AddMarkupContent(7, " <script src='_framework/blazor.server.js'></script>");
builder.CloseElement();
builder.CloseElement();
}
}
public class MetaTags
{
public string Title { get; set; } = "";
public string Description { get; set; } = "";
}
}
Head.razor component to set the meta tags
@inject AppState _appState
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@_appState.MetaTags.Title</title>
<meta name="description" content="@_appState.MetaTags.Description">
<base href="~/" />
<link rel="stylesheet" href="/css/bootstrap/bootstrap.min.css" />
<link href="/css/site.css" rel="stylesheet" />
</head>
@code {
protected override async Task OnInitializedAsync()
{
_appState.OnChange += StateHasChanged;
}
}
Body.razor
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
AppState.cs
using System;
namespace BlazorMetaTags
{
public class AppState
{
public MetaTags MetaTags { get; private set; } = new MetaTags();
public event Action OnChange;
public void SetMetaTags(MetaTags metatags)
{
MetaTags = metatags;
NotifyStateChanged();
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<AppState>();
services.AddSingleton<WeatherForecastService>();
}
2
Answers
As suggested by Meisam Dehghan in a comment, the correct solution is to change
to
This worked for me!
I offer my decision on this issue. This solution has several advantages:
Here is my tested and working solution:
According to a comment from Brad Bamford I have added an example of GetSeoDataDB method where I replaced switch with a query to the database for dynamically retrieve SEO data from DB depending on the page using Dapper: