Convert Blazor Server App to Blazor Web App
This skill helps an agent convert a pre-.NET 8 Blazor Server app into a .NET 8+ Blazor Web App. The old hosting model uses AddServerSideBlazor/MapBlazorHub with a _Host.cshtml Razor Page as the entry point. The new Blazor Web App model uses AddRazorComponents/MapRazorComponents with an App.razor root component, enabling per-component render modes, enhanced navigation, streaming rendering, and other .NET 8+ features. The converted app uses InteractiveServer render mode to preserve existing interactive behavior.
When to Use
- Migrating a Blazor Server app from .NET 6 or .NET 7 to .NET 8+
- App currently uses
AddServerSideBlazor()andMapBlazorHub()inProgram.cs(orStartup.cs) - App uses
Pages/_Host.cshtml(or_Host.razor) as the host page with Component Tag Helpers - Want to adopt new Blazor Web App features while keeping interactive server rendering
When Not to Use
- The app already uses
AddRazorComponentsandMapRazorComponents. It is already a Blazor Web App — no conversion is needed. Stop here and tell the user the app is already using the Blazor Web App model. - Blazor WebAssembly or hosted Blazor WebAssembly app — these have a different migration path
- The app should stay on the legacy Blazor Server hosting model (just update TFM and packages)
- The app targets .NET Framework — it must be migrated to .NET first
Inputs
Workflow
Commit strategy: Commit after each logical step so the migration is reviewable and bisectable.
Step 1: Update the project file
Update the .csproj file:
- Change the Target Framework Moniker (TFM) to the target version:
- Update all
Microsoft.AspNetCore.*,Microsoft.EntityFrameworkCore.*,Microsoft.Extensions.*, andSystem.Net.Http.Jsonpackage references to the matching version.
For non-Blazor project file changes (nullable reference types, implicit usings, HTTP/3 support, etc.), see the general ASP.NET Core migration guide.
Step 2: Create Routes.razor from App.razor
The old App.razor contains the <Router> component. This content moves to a new Routes.razor file so that App.razor can become the root HTML document component.
- Create a new file
Routes.razorin the project root. - Move the entire content of
App.razorintoRoutes.razor. - If the content is wrapped in
<CascadingAuthenticationState>, remove that wrapper (it will be replaced by a service in Step 5). - Leave
App.razorempty for the next step.
The resulting Routes.razor should look similar to:
If the app uses <AuthorizeRouteView> instead of <RouteView>, keep it — it works the same way in Blazor Web Apps.
Step 3: Convert _Host.cshtml to App.razor
Move the HTML shell from Pages/_Host.cshtml into the now-empty App.razor and transform it from a Razor Page into a Razor component:
-
Remove Razor Page directives — delete
@page "/",@using Microsoft.AspNetCore.Components.Web,@namespace, and@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers. -
Add component injection — if using environment-conditional error UI, add:
-
Fix the base tag — replace
<base href="~/" />with<base href="/" />. -
Replace HeadOutlet Component Tag Helper — replace:
with:
-
Replace App Component Tag Helper with Routes — replace:
with:
-
Replace Environment Tag Helpers — replace:
with:
-
Update the Blazor script — replace:
with:
-
Add render mode import — add to
_Imports.razor: -
Delete
Pages/_Host.cshtml(andPages/_Host.cshtml.csif it exists).
Prerendering note: If the original app used render-mode="Server" (not "ServerPrerendered"), prerendering was disabled. Preserve this by using new InteractiveServerRenderMode(prerender: false) instead of InteractiveServer for both HeadOutlet and Routes.
Step 4: Update Program.cs
Make the following changes to Program.cs (or Startup.cs if the app uses the older hosting pattern):
-
Replace Blazor Server services — replace:
with:
If
AddServerSideBlazorhad options configured (e.g., circuit options, hub options, detailed errors), migrate them toAddInteractiveServerComponents: -
Replace Blazor endpoint mapping — replace:
with:
Ensure there is a
usingstatement for the project's root namespace so thatAppresolves to theApp.razorcomponent. -
Remove the fallback route — delete:
-
Remove explicit routing middleware — delete if present:
Endpoint routing is the default and explicit
UseRouting()is no longer needed. -
Add antiforgery middleware — add after
UseAuthentication/UseAuthorizationif present:AddRazorComponentsregisters antiforgery services automatically, but the middleware must be explicitly added to the pipeline. Without it, form POST requests fail with 400 errors.
Step 5: Migrate CascadingAuthenticationState (if present)
If the app used <CascadingAuthenticationState> to wrap the router:
- Remove the
<CascadingAuthenticationState>component wrapper (already done in Step 2 if following this workflow). - Add the cascading authentication state service in
Program.cs:
The component wrapper approach does not work across render mode boundaries in Blazor Web Apps. The service-based approach provides Task<AuthenticationState> as a cascading value to all components regardless of render mode.
Step 6: Recommended improvements (optional)
These are optional modernization improvements — not required for the conversion to work. If you suggest any of these, state explicitly that they are optional.
- Replace
UseStaticFileswithMapStaticAssets(.NET 9+):app.MapStaticAssets()provides optimized static file serving with fingerprinting, pre-compression, and content-based ETags. See MapStaticAssets documentation. - Add
@attribute [StreamRendering]to pages with async data loading (OnInitializedAsync) for improved perceived performance. The page renders its initial synchronous content immediately and re-renders when async data arrives. - Update CSS isolation bundle reference if the
<link>tag referenced a_Hostassembly name; ensure it matches the project's actual assembly name:<link href="{AssemblyName}.styles.css" rel="stylesheet" />. - For other non-Blazor improvements (minimal hosting, HTTP/3, output caching, etc.), see the general ASP.NET Core migration guide.
Step 7: Verify the migration
- Build the project targeting the new framework. Confirm no compile errors.
- Search for remaining references to removed APIs:
AddServerSideBlazorMapBlazorHubMapFallbackToPageblazor.server.js_Host.cshtml
- Run the app and verify:
- Pages load and render correctly
- Interactive features work (forms, event handlers, SignalR circuits)
- Navigation between pages works
- Authentication and authorization flows work if present
- Run existing tests.
Validation
- No references to
AddServerSideBlazorremain - No references to
MapBlazorHubremain - No references to
MapFallbackToPage("/_Host")remain - No references to
blazor.server.jsremain -
Pages/_Host.cshtmlhas been deleted -
App.razorserves as the root component with a full HTML document structure -
Routes.razorcontains the<Router>configuration -
Program.csusesAddRazorComponents().AddInteractiveServerComponents() -
Program.csusesMapRazorComponents<App>().AddInteractiveServerRenderMode() -
app.UseAntiforgery()is present in the middleware pipeline - If the app used
<CascadingAuthenticationState>, it has been replaced withAddCascadingAuthenticationState()service registration - App builds and runs successfully on the target framework
Common Pitfalls
More Info
- Convert a Blazor Server app into a Blazor Web App — the official step-by-step migration guide
- ASP.NET Core Blazor render modes — understanding InteractiveServer, InteractiveWebAssembly, and InteractiveAuto
- Migrate CascadingAuthenticationState to services — replacing the component wrapper with a service
- MapStaticAssets — optimized static file serving in .NET 9+
- Migrate from ASP.NET Core 7.0 to 8.0 — general migration guide for all ASP.NET Core changes
- Stream rendering with Blazor —
@attribute [StreamRendering]for async data loading - Cascading values and render mode boundaries — why cascading parameters do not cross render mode boundaries


