For years,.NET developers have been building rich, interactive web UIs with Blazor, a powerful framework that brings C# to the browser. Historically, this involved a significant upfront decision: choose Blazor Server for a fast-loading, server-connected application powered by SignalR, or choose Blazor WebAssembly for a true client-side experience that can run offline but comes with a larger initial download. This choice, often called the “hosting model dilemma,” forced developers to pick a side, trading off one set of benefits for another.
With the release of.NET 8, this dichotomy has been resolved. The new Blazor Web App model, colloquially known as Blazor United, unifies the server and client hosting models into a single, cohesive framework. It introduces a flexible component model where developers can choose the best rendering strategy on a per-component or per-page basis. This is not just an incremental update; it’s a paradigm shift that positions Blazor as a true full-stack web UI framework, capable of building everything from lightning-fast, static marketing pages to highly interactive, complex single-page applications (SPAs).
This in-depth tutorial will guide you through building a modern, full-stack task management application using the new Blazor Web App model. We will explore the nuances of each render mode, dissect the new project structure, tackle the complexities of state management across rendering boundaries, and secure the application with ASP.NET Core Identity.
Section 1: Understanding the New Blazor: Render Modes Explained
The cornerstone of the new Blazor model is the introduction of multiple render modes. Instead of a single, application-wide hosting model, you can now decide precisely how and where each part of your UI should be rendered. This allows for fine-grained performance optimization and a better user experience.
Static Server-Side Rendering (SSR)
This is the new default render mode in a Blazor Web App. When a component is statically rendered, the ASP.NET Core server executes the Razor component code and renders it directly to an HTML response stream.
- How it Works: The server sends pure, static HTML to the browser. There is no ongoing SignalR connection, and no.NET WebAssembly runtime is downloaded. Event handling (like
@onclick) is not possible with C# methods. - Advantages: This mode offers the fastest possible initial load time and is inherently SEO-friendly, as search engine crawlers receive fully rendered HTML. It’s perfect for public-facing, content-heavy pages like homepages, blogs, or product marketing pages.
Interactive Server Rendering
This mode is the evolution of the classic Blazor Server hosting model. It brings full interactivity to your components, with all UI logic still executing on the server.
- How it Works: The component is initially rendered on the server (and can be pre-rendered for a faster first paint). A persistent SignalR connection is then established between the client and the server. All user interactions (button clicks, form submissions) are sent over this connection to the server, which processes the events, re-renders the component, and sends the UI diff back to the client to update the DOM.
- Advantages: It provides a rich, interactive experience with a very small initial payload, as no.NET runtime needs to be downloaded to the client. It also has full access to server-side resources and the complete.NET runtime.
Interactive WebAssembly (WASM) Rendering
This is the evolution of the classic Blazor WebAssembly hosting model, enabling rich client-side interactivity that can even work offline.
- How it Works: The component, its dependencies, and a lightweight.NET runtime compiled to WebAssembly are downloaded to the client’s browser. All UI interactions and logic are then executed directly in the browser, completely client-side.
- Advantages: This mode enables true SPA-like experiences, can function offline (once loaded), and offloads processing from the server to the client. It’s ideal for complex, stateful components or applications that need to be highly responsive without network latency.
Interactive Auto Rendering
This is the hybrid mode that truly embodies the “best of both worlds” promise of Blazor United. It intelligently switches between server and client rendering to optimize for both initial load time and long-term interactivity.
- How it Works: The first time a user visits a page with an “Auto” component, it uses the Interactive Server mode. This provides a fast, interactive experience almost immediately. While the user is interacting with the server-rendered component, the.NET WebAssembly runtime and component bundle are downloaded in the background and cached by the browser. On subsequent visits, the browser will use the cached assets and switch to the Interactive WebAssembly mode, eliminating the need for a persistent SignalR connection.
- Advantages: It provides the fast initial load of Blazor Server with the long-term benefits of Blazor WebAssembly (offline capability, reduced server load), offering an optimal user experience across the board.
This new model fundamentally reframes Blazor as a Progressive Enhancement framework. Developers are encouraged to start with the fastest, simplest mode Static SSR and then “progressively enhance” specific parts of the UI by applying interactive render modes to individual components. This is a powerful departure from the traditional SPA model, where the entire application is a heavy client-side bundle by default. It allows for a more granular and performance-conscious approach to web development, directly competing with modern JavaScript meta-frameworks.
Table 2: Blazor.NET 8 Render Modes – A Comparative Guide
| Feature | Static SSR | Interactive Server | Interactive WebAssembly | Interactive Auto |
| Render Location | Server | Server | Client (WASM) | Server, then Client |
| Interactivity | No | Yes (via SignalR) | Yes (in-browser) | Yes (SignalR, then WASM) |
| Initial Load Time | Fastest | Fast | Slowest | Fast |
| Payload Size | Smallest | Small | Largest | Small (initially) |
| Offline Support | No | No | Yes | Yes (after first visit) |
| SEO Friendliness | Excellent | Good (with pre-rendering) | Fair (requires pre-rendering) | Good (with pre-rendering) |
| Server Load | Low | High (maintains state per user) | Low (only serves static files) | High (first visit), then Low |
| Ideal Use Case | Content pages, blogs, marketing sites | LOB apps, dashboards, apps needing server access | Rich SPAs, offline apps, PWAs | General purpose apps wanting the best of both worlds |
Export to Sheets
Section 2: Project Structure and Setup
The new capabilities are powered by a new project template: the Blazor Web App. Understanding its structure is key to effectively using the different render modes.
Creating the Project
You can create a new Blazor Web App from the.NET CLI:
Bash
dotnet new blazor -o BlazorUnitedApp -int Auto
The -int Auto (or --interactivity Auto) flag is crucial. It tells the template to set up the project for Interactive Auto mode, which includes support for all other interactive modes. This will generate a solution with two projects.
Dissecting the Solution
The generated solution contains two main projects, which is a fundamental concept to grasp :
BlazorUnitedApp(Server Project): This is the main ASP.NET Core project that hosts the application. It contains:- Components and pages that will be rendered statically on the server (
@rendermode @static). - Components and pages that will be rendered interactively on the server (
@rendermode @InteractiveServer). - The
Program.csfile, which configures all the services and middleware for the application. - Backend logic, API endpoints, and data access code.
- Components and pages that will be rendered statically on the server (
BlazorUnitedApp.Client(Client Project): This is a separate project that gets compiled to WebAssembly. It contains:- Components and pages that are intended to run on the client (
@rendermode @InteractiveWebAssemblyor@rendermode @InteractiveAuto). - Any client-side logic that needs to run in the browser’s WASM sandbox. Code in this project cannot directly access server-side resources like a database; it must call an API hosted in the server project.
- Components and pages that are intended to run on the client (
Configuring Render Modes in Program.cs
The magic of enabling these different modes happens in the server project’s Program.cs file. The template sets up the following essential services:
C#
// BlazorUnitedApp/Program.cs
// 1. Add services for Razor component rendering.
builder.Services.AddRazorComponents()
// 2. Add services for Interactive Server components.
.AddInteractiveServerComponents()
// 3. Add services for Interactive WebAssembly components.
.AddInteractiveWebAssemblyComponents();
//...
var app = builder.Build();
//...
// 4. Map the root component and enable render modes.
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(BlazorUnitedApp.Client._Imports).Assembly);
Here’s what each part does :
AddRazorComponents(): Registers the core services needed to render Razor components.AddInteractiveServerComponents(): Adds the services required for Interactive Server mode, including the SignalR hub.AddInteractiveWebAssemblyComponents(): Configures the project to find and serve the assets from the.Clientproject for WASM rendering.MapRazorComponents<App>(): Sets up the endpoint that renders the rootAppcomponent and enables the server and WASM render modes for the application.
Section 3: Building a Task Management Application
Let’s apply these concepts by building a simple “Todo” application. We’ll start by creating the basic layout and pages.
Layout and Shared Components
In the server project (BlazorUnitedApp), open the Components/Layout folder. Here you’ll find MainLayout.razor and NavMenu.razor. These components define the overall structure and navigation of the application. Since they apply to all pages regardless of render mode, they reside in the server project.
Creating a Static “About” Page
To see static SSR in action, let’s create a simple, non-interactive page. In the Components/Pages folder of the server project, create a new Razor component named About.razor:
Razor CSHTML
@page "/about"
<PageTitle>About</PageTitle>
<h1>About Our Todo App</h1>
<p>This application is a demonstration of the new Blazor Web App model in.NET 8, showcasing different rendering modes.</p>
When you run the application and navigate to /about, this page will be rendered on the server and sent as static HTML. No SignalR connection will be established.
Creating an Interactive “Todo” Page
Now for the main feature. In the Components/Pages folder of the server project, create TodoList.razor. We’ll start by making it interactive on the server.
Razor CSHTML
@page "/todo"
@rendermode InteractiveServer
<PageTitle>Todo List</PageTitle>
<h1>Todo List</h1>
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" @bind="todo.IsDone" />
<span style="@(todo.IsDone? "text-decoration: line-through;" : "")">@todo.Title</span>
</li>
}
</ul>
<input @bind="newTodoTitle" placeholder="Something to do" />
<button @onclick="AddTodo">Add todo</button>
@code {
private List<TodoItem> todos = new();
private string? newTodoTitle;
private void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodoTitle))
{
todos.Add(new TodoItem { Title = newTodoTitle });
newTodoTitle = string.Empty;
}
}
public class TodoItem
{
public string? Title { get; set; }
public bool IsDone { get; set; }
}
}
The @rendermode InteractiveServer directive at the top is key. It tells the Blazor framework to enable interactivity for this component using the server hosting model. When you run the app and navigate to /todo, a SignalR connection will be established, and you’ll be able to add and check off tasks.
For more complex enterprise applications, building a rich UI from scratch can be time-consuming. Professional component suites like (https://www.telerik.com/blazor-ui) provide a comprehensive library of over 110 high-performance, pre-built components from advanced data grids to charts and forms that can dramatically accelerate development.
Section 4: Implementing Pages with Different Render Modes
Now, let’s explore the flexibility of the new model by moving our interactive components to the client.
Moving to WebAssembly
To make our TodoList component run entirely in the browser, we need to move it to the .Client project.
- Move the Code: Cut the
TodoList.razorfile from the server project’sComponents/Pagesfolder and paste it into theBlazorUnitedApp.Clientproject’sPagesfolder. - Change the Render Mode: In
TodoList.razor, change the directive to@rendermode InteractiveWebAssembly.
That’s it. When you run the application and navigate to /todo, the browser will now download the.NET runtime and your component’s assembly. The component will be fully interactive, but all processing now happens on the client.
The “Auto” Magic
The real power comes from the InteractiveAuto mode. Simply change the directive again:
Razor CSHTML
@page "/todo"
@rendermode InteractiveAuto
Now, the first time you visit the /todo page, it will load using Interactive Server. You can verify this in your browser’s developer tools by observing the active WebSocket connection. While you’re using the app, the WASM files will download in the background. If you then do a hard refresh (Ctrl+F5) or navigate away and come back, the page will now load using Interactive WebAssembly, and the WebSocket connection will no longer be established for this component.
Section 5: Advanced State Management Across Render Modes
The flexibility of mixing render modes introduces a new challenge: managing state. The choice of render mode becomes the primary factor dictating which state management strategies are viable. A static component has no state. A server-interactive component’s state lives in a server-side circuit. A WASM component’s state lives in the browser’s memory. The InteractiveAuto mode is the most complex, as state may need to be handed off from the server to the client.
The State Handoff Problem with “Auto” Mode
When a component in InteractiveAuto mode first renders on the server, it might fetch data from a database. When it then transitions to WebAssembly on the client, that state is lost, and the client-side component would need to re-fetch the same data via an API call. This is inefficient.
Using Persistent Component State
Blazor provides the PersistentComponentState service to solve this exact problem. It allows state to be serialized on the server and passed down to the client during the initial HTML render, where it can be rehydrated by the WASM component.
Let’s modify our TodoList.razor component (now in the .Client project) to use it.
Razor CSHTML
@page "/todo"
@rendermode InteractiveAuto
@inject PersistentComponentState ApplicationState
@implements IDisposable
//... UI markup...
@code {
private List<TodoItem> todos = new();
private string? newTodoTitle;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistTodos);
if (!ApplicationState.TryTakeFromJson<List<TodoItem>>("todos", out var restoredTodos))
{
// No state restored, so we're on the server. Fetch data.
// In a real app, this would be an async call to a service.
todos = new List<TodoItem>
{
new TodoItem { Title = "Learn Blazor Render Modes" },
new TodoItem { Title = "Master State Management" }
};
}
else
{
// State was restored, so we're on the client.
todos = restoredTodos!;
}
}
private Task PersistTodos()
{
ApplicationState.PersistAsJson("todos", todos);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
//... AddTodo and TodoItem class...
}
This logic ensures that data is fetched only once on the server during the initial render. The state is then “persisted” into the HTML page. When the WASM component initializes on the client, it retrieves this state, avoiding a redundant API call.
Global State with a Scoped Service
For state that needs to be shared across many components, like user preferences or a shopping cart, a dependency-injected state container service is a common pattern. In a Blazor Server circuit, a
Scoped service is unique to a user’s session, making it a simple and effective state container. However, making this state available to client-side components requires persisting it, just as we did with component-level state.
Section 6: Securing Your Full-Stack App with ASP.NET Core Identity
Authentication and authorization are critical for any real-world application. The Blazor Web App template provides first-class integration with ASP.NET Core Identity.
Scaffolding Identity
The easiest way to get started is to create the project with authentication enabled from the start:
Bash
dotnet new blazor -o BlazorUnitedApp -au Individual -int Auto
The -au Individual flag scaffolds all the necessary ASP.NET Core Identity components, including a DbContext for user data, and UI pages for registration, login, and account management.
Understanding the Identity UI
It’s important to note that the scaffolded Identity UI (/Components/Account/Pages) consists of Razor Pages, not interactive Blazor components. This is intentional. Core identity operations like login and registration are fundamentally tied to the HTTP request/response cycle, involving redirects and cookie management, which are handled by the ASP.NET Core backend.
Authorizing Access
Once Identity is set up, you can secure your application using standard ASP.NET Core authorization mechanisms:
[Authorize]Attribute: Apply this attribute to routable components (@page) to require authentication for the entire page.<AuthorizeView>Component: Use this component within your Razor markup to conditionally render UI elements based on the user’s authentication state (e.g., show a “Login” button for anonymous users and a “Logout” button for authenticated users).
Authentication State Across Boundaries
A key piece of the puzzle is how the user’s authentication state is shared from the server to client-side (WASM) components. The template automatically configures a PersistentAuthenticationStateProvider. This service works similarly to the PersistentComponentState we saw earlier. It serializes the user’s authentication state on the server and makes it available for the client-side Blazor app to rehydrate. This ensures that when a component switches to InteractiveWebAssembly mode, it still knows who the user is without needing a separate login process on the client.
Section 7: Deployment to Azure and Further Learning
Deploying a Blazor Web App is straightforward, as it’s fundamentally an ASP.NET Core application.
Azure App Service Deployment
You can deploy your application to Azure App Service directly from Visual Studio or by setting up a CI/CD pipeline with GitHub Actions. When deploying an app that uses Interactive Server or Interactive Auto modes, it’s critical to enable the “Web Sockets” setting in your Azure App Service configuration, as this is required for the SignalR connection to function correctly.
Conclusion
The new Blazor Web App model in.NET 8, or Blazor United, represents a monumental step forward for.NET web development. By unifying server and client rendering into a single, flexible framework, it empowers developers to build highly optimized, full-stack web applications with C# that can be tailored to any scenario. By mastering the different render modes and understanding how to manage state across them, you can build web applications that are faster, more capable, and more maintainable than ever before.
For developers who want to dive even deeper into building robust and reusable Blazor components, (https://amzn.to/4lH5IVi) is an excellent, practical guide that covers component design, forms, validation, and JavaScript interoperability in great detail.

Leave a comment