Edit

Share via


Prerender ASP.NET Core Razor components

Note

This isn't the latest version of this article. For the current release, see the .NET 9 version of this article.

Important

This information relates to a pre-release product that may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

For the current release, see the .NET 9 version of this article.

This article explains Razor component prerendering scenarios for server-rendered components in Blazor Web Apps and Blazor Server apps.

Prerendering is the process of statically rendering page content from the server to deliver HTML to the browser as quickly as possible. After the prerendered content is quickly displayed to the user, interactive content with active event handlers are rendered, replacing any content that was rendered previously. Prerendering can also improve Search Engine Optimization (SEO) by rendering content for the initial HTTP response that search engines use to calculate page rank.

Prerendering is enabled by default for interactive components.

Internal navigation with interactive routing doesn't use prerendering because the page is already interactive. For more information, see Static versus interactive routing and Interactive routing and prerendering.

OnAfterRender{Async} component lifecycle events aren't called when prerendering, only after the component renders interactively.

Disable prerendering

Prerendering can complicate an app because the app's Razor components must render twice: once for prerendering and once for setting up interactivity. If the components are set up to run on WebAssembly, then you also must design your components so that they can run from both the server and the client.

To disable prerendering for a component instance, pass the prerender flag with a value of false to the render mode:

  • <... @rendermode="new InteractiveServerRenderMode(prerender: false)" />
  • <... @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
  • <... @rendermode="new InteractiveAutoRenderMode(prerender: false)" />

To disable prerendering in a component definition:

  • @rendermode @(new InteractiveServerRenderMode(prerender: false))
  • @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
  • @rendermode @(new InteractiveAutoRenderMode(prerender: false))

To disable prerendering for the entire app, indicate the render mode at the highest-level interactive component in the app's component hierarchy that isn't a root component.

For apps based on the Blazor Web App project template, a render mode assigned to the entire app is specified where the Routes component is used in the App component (Components/App.razor). The following example sets the app's render mode to Interactive Server with prerendering disabled:

<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Also, disable prerendering for the HeadOutlet component in the App component:

<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Making a root component, such as the App component, interactive with the @rendermode directive at the top of the root component's definition file (.razor) isn't supported. Therefore, prerendering can't be disabled directly by the App component.

Disabling prerendering using the preceding techniques only takes effect for top-level render modes. If a parent component specifies a render mode, the prerendering settings of its children are ignored.

Persist prerendered state

Without persisting prerendered state, state used during prerendering is lost and must be recreated when the app is fully loaded. If any state is created asynchronously, the UI may flicker as the prerendered UI is replaced when the component is rerendered. For guidance on how to persist state during prerendering, see ASP.NET Core Blazor prerendered state persistence.

Client-side services fail to resolve during prerendering

Assuming that prerendering isn't disabled for a component or for the app, a component in the .Client project is prerendered on the server. Because the server doesn't have access to registered client-side Blazor services, it isn't possible to inject these services into a component without receiving an error that the service can't be found during prerendering.

For example, consider the following Home component in the .Client project in a Blazor Web App with global Interactive WebAssembly or Interactive Auto rendering. The component attempts to inject IWebAssemblyHostEnvironment to obtain the environment's name.

@page "/"
@inject IWebAssemblyHostEnvironment Environment

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<p>
    Environment: @Environment.Environment
</p>

No compile time error occurs, but a runtime error occurs during prerendering:

Cannot provide a value for property 'Environment' on type 'BlazorSample.Client.Pages.Home'. There is no registered service of type 'Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment'.

This error occurs because the component must compile and execute on the server during prerendering, but IWebAssemblyHostEnvironment isn't a registered service on the server.

Consider any of the following approaches to address this scenario:

Register the service on the server in addition to the client

If the service supports server execution, register the service on the server in addition to the client so that it's available during prerendering. For an example of this scenario, see the guidance for HttpClient services in the Blazor Web App external web APIs section of the Call web API article.

Inject a service that the app can use during prerendering

In some cases, the app can use a service on the server during prerendering and a different service on the client.

For example, the following code obtains the app's environment whether the code is running on the server or on the client by injecting IHostEnvironment from the Microsoft.Extensions.Hosting.Abstractions NuGet package:

private string? environmentName;

public Home(IHostEnvironment? serverEnvironment = null, 
    IWebAssemblyHostEnvironment? wasmEnvironment = null)
{
    environmentName = serverEnvironment?.EnvironmentName;
    environmentName ??= wasmEnvironment?.Environment;
}

However, this approach adds an additional dependency to the client project that isn't needed.

Make the service optional

Make the service optional if it isn't required during prerendering using either of the following approaches.

The following example uses constructor injection of IWebAssemblyHostEnvironment:

private string? environmentName;

public Home(IWebAssemblyHostEnvironment? env = null)
{
    environmentName = env?.Environment;
}

Alternatively, inject IServiceProvider to optionally obtain the service if it's available:

@page "/"
@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@inject IServiceProvider Services

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<p>
    <b>Environment:</b> @environmentName
</p>

@code {
    private string? environmentName;

    protected override void OnInitialized()
    {
        if (Services.GetService<IWebAssemblyHostEnvironment>() is { } env)
        {
            environmentName = env.Environment;
        }
    }
}

Create a service abstraction

If a different service implementation is needed on the server, create a service abstraction and create implementations for the service in the server and client projects. Register the services in each project. Inject the custom service abstraction into components where needed. The component then depends solely on the custom service abstraction.

In the case of IWebAssemblyHostEnvironment, we can reuse the existing interface instead of creating a new one:

ServerHostEnvironment.cs:

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Components;

public class ServerHostEnvironment(IWebHostEnvironment env, NavigationManager nav) : 
    IWebAssemblyHostEnvironment
{
    public string Environment => env.EnvironmentName;
    public string BaseAddress => nav.BaseUri;
}

In the server project's Program file, register the service:

builder.Services.TryAddScoped<IWebAssemblyHostEnvironment, ServerHostEnvironment>();

At this point, the IWebAssemblyHostEnvironment service can be injected into an interactive WebAssembly or Auto component that is also prerendered from the server.

Disable prerendering for the component

Disable prerendering for the component or for the entire app. For more information, see the Disable prerendering section.