Aspnetcore: Influencing HTML Head from a Blazor component

Created on 22 May 2019  路  20Comments  路  Source: dotnet/aspnetcore

In Blazor apps the root of the HTML document is typically controlled by the layout component. If other components need to influence the contents of the HTML Head tag (e.g. to add or change Meta or Title tags) there isn't really an established patterns to do that today.

Components Big Rock Done area-blazor enhancement

Most helpful comment

Hopefully it will work during prerendering in Blazor client side app. It is needed for SEO and social sites integration.

All 20 comments

Hopefully it will work during prerendering in Blazor client side app. It is needed for SEO and social sites integration.

Very lacking this functionality today

Sometimes it's even needed to adjust the <body> tag (most of the time the css classes due to theme requirements. For example the case where you open/close sidebars and it's based on the css class of the body tag. Currently I use the following code snipped to do so via Interop:

window.application = {
    toggleAsideOnMobile() {
        const className = 'kt-aside--on';
        const body = document.getElementsByTagName('body')[0];
        const aside = document.getElementById('kt_aside');
        body.classList.toggle(className);
        aside.classList.toggle(className);
        return true;
    },
    toggleTopbarOnMobile() {
        const className = 'kt-header__topbar--mobile-on';
        const body = document.getElementsByTagName('body')[0];
        body.classList.toggle(className);
        return true;
    }
};

@vertonghenb unless it _has_ to be the <body> tag, you don't need JS for that. Inside the components, you can simply emit different CSS classes.

Blazor doesn't have built-in tooling for that, but you can use something like CssBuilder.

@chucker Yes, I know but it _has_ to be the body tag.

I stumbled upon this today, what I really need is the ability to write in the browser history, and I guess it simply takes to add some parameters to what already exist, the layout of the page can inspect the route and generate SEO metadata during load... It still is in a MVC controller after all...

I suspect SEO engines just do a lot of requests without using JavaScript so... I don't see how that is useful, here a workaround for those who want per request page title or meta

File: _Host.cshtml

@page "/"
@namespace MyWebApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject Data.ApplicationDbContext  ApplicationDbContext

@{
    var seoEntity = ApplicationDbContext.ExampleSeoTable
        .SingleOrDefault(x => x.path == this.HttpContext.Request.Path);
    var title = seoEntity?.Title ?? "This page have no title in the db";
}
<!DOCTYPE html>
<html lang="en">
<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/site.css" rel="stylesheet" />
</head>
...
</html>

@vertonghenb I don't see why anything need to be modified to achieve what you ask :

File _Host.cshtml

<!DOCTYPE html>
<html lang="en">
...
</head>
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</html>

Then

File App.razor

@code { ... }
<body class="@someClasses">
<Router AppAssembly="@typeof(Program).Assembly">
...
</Router>
</body>

@TrabacchinLuigi I don鈥檛 know that was possible, awesome! Thanks for sharing!

@TrabacchinLuigi Hmm, I'm not sure if this would work, please correct me if I'm wrong. Since components can't contain <script> tags, if you place the <body> tag inside a component then you have nowhere to place <script> tags. <script> tags are meant to be in the <body> are they not?

@TrabacchinLuigi Hmm, I'm not sure if this would work, please correct me if I'm wrong. Since components can't contain <script> tags, if you place the <body> tag inside a component then you have nowhere to place <script> tags. <script> tags are meant to be in the <body> are they not?

That's a good argument!

@dotkiwi @vertonghenb as far as i know script tags can be placed in head tag, inside body tag or after it, it just depends on when you want that script to be available and start executing (also how bad is that script, the trend of putting scripts at the end of the page started because of people writing bad libs not tied to events, and by doing so the scripts worked only if they where "rendered by the browser" after the rest of the dom), in this case it should not matter, the page is very lightweight and the majority of the content is downloaded later by Blazor ...

Is there anyway now to add script and css references from a Razor component now? I have several Razor components with their own JavaScript files and css files. If i use these components i'll have to link to their JavaScript and css files in "index.html" manually. Is there some way this can be done from the Razor component?

Another use case to document that the lack of this feature is impacting right now is web analytics solutions. Today, in systems like Application Insights, page views are tracked via the JavaScript SDK which can be configured to auto-track route changes and report those as page views (no problem there). The issue is the document title is used as the page view name, so looking at page views in your analytics dashboard just shows all views being for the application home page (as the title never changes).

Is there anyway now to add script and CSS references from a Razor component now? I have several Razor components with their own JavaScript files and css files. If i use these components i'll have to link to their JavaScript and css files in "index.html" manually. Is there some way this can be done from the Razor component?

This is a very reasonable request. How can I add style links inside a component? FAQ page needs custom style for example. The Head tag is in _Host.cshtml and as far as I understand there is no way right now?

This feature will be very useful in supporting RTL and LTR layout switching.

@GioviQ I've tried this component.

But this only works in the Browser and we can find that the title changes in the browser's tab. But the title WILL NOT UPDATE if someone tries to share a link through some social apps, because the title is updated using the Web Socket but the social apps only fetch the title once and will not update further.

@walterlv I think you're right, so what is the solution?

need a way for server to render prerender component base on first time request url. this will help seo to fetch necessary info, meanwhile allow user to serve the seamlessly

To add to the example by @vertonghenb and when using MapFallbackToPage, the _Host.cshtml file is evaluated, then if present, the cshtml layout file is evaluated. In addition, a dependency injected service can be accessed by the blazor ServerPrerendered, and the layout page.

So to influence the HTML Head when using ServerPrerendered, you make sure you are constructing the HTML Head in a cshtml layout file. You then use a transient dependency injected service to carry the value from Blazor to the layout.

@vertonghenb I don't see why anything need to be modified to achieve what you ask :

File _Host.cshtml

<!DOCTYPE html>
<html lang="en">
...
</head>
@(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</html>

Then

File App.razor

@code { ... }
<body class="@someClasses">
<Router AppAssembly="@typeof(Program).Assembly">
...
</Router>
</body>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kevenvz picture Kevenvz  路  3Comments

markrendle picture markrendle  路  3Comments

fayezmm picture fayezmm  路  3Comments

ermithun picture ermithun  路  3Comments

farhadibehnam picture farhadibehnam  路  3Comments