Install
$ agentstack add skill-dotnet-skills-convert-blazor-server-to-webapp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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
| Input | Required | Description | |-------|----------|-------------| | Blazor Server project | Yes | The .csproj and source files of the Blazor Server app | | Target framework | Yes | .NET 8 or later (e.g., net8.0, net9.0, net10.0) | | Program.cs or Startup.cs | Yes | The app's service and middleware configuration | | _Host.cshtml location | Recommended | Usually Pages/_Host.cshtml; may be _Host.razor in some projects |
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:
``xml net8.0 ``
- 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 ` 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 ``, 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:
Sorry, there's nothing at this address.
If the app uses ` instead of `, 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:
``razor @inject IHostEnvironment Env ``
- Fix the base tag — replace `
with`.
- Replace HeadOutlet Component Tag Helper — replace:
```html
`` with: ``razor
```
- Replace App Component Tag Helper with Routes — replace:
```html
`` with: ``razor
```
- Replace Environment Tag Helpers — replace:
```html
An error has occurred. This application may no longer respond until reloaded.
An unhandled exception has occurred. See browser dev tools for details.
`` with: ``razor @if (Env.IsDevelopment()) {
An unhandled exception has occurred. See browser dev tools for details.
} else {
An error has occurred. This app may no longer respond until reloaded.
} ```
- Update the Blazor script — replace:
```html
`` with: ``html
```
- Add render mode import — add to
_Imports.razor:
``razor @using static Microsoft.AspNetCore.Components.Web.RenderMode ``
- 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:
``csharp builder.Services.AddServerSideBlazor(); ` with: `csharp builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); ``
If AddServerSideBlazor had options configured (e.g., circuit options, hub options, detailed errors), migrate them to AddInteractiveServerComponents: ```csharp // Old: builder.Services.AddServerSideBlazor(options => { options.DetailedErrors = true; options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); });
// New: builder.Services.AddRazorComponents() .AddInteractiveServerComponents(options => { options.DetailedErrors = true; options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); }); ```
- Replace Blazor endpoint mapping — replace:
``csharp app.MapBlazorHub(); ` with: `csharp app.MapRazorComponents() .AddInteractiveServerRenderMode(); ``
Ensure there is a using statement for the project's root namespace so that App resolves to the App.razor component.
- Remove the fallback route — delete:
``csharp app.MapFallbackToPage("/_Host"); ``
- Remove explicit routing middleware — delete if present:
``csharp app.UseRouting(); ` Endpoint routing is the default and explicit UseRouting()` is no longer needed.
- Add antiforgery middleware — add after
UseAuthentication/UseAuthorizationif present:
``csharp app.UseAntiforgery(); ` AddRazorComponents` registers 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 `` to wrap the router:
- Remove the `` component wrapper (already done in Step 2 if following this workflow).
- Add the cascading authentication state service in
Program.cs:
``csharp builder.Services.AddCascadingAuthenticationState(); ``
The component wrapper approach does not work across render mode boundaries in Blazor Web Apps. The service-based approach provides Task 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 `
tag referenced a_Hostassembly name; ensure it matches the project's actual assembly name:`. - 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 `` configuration - [ ]
Program.csusesAddRazorComponents().AddInteractiveServerComponents() - [ ]
Program.csusesMapRazorComponents().AddInteractiveServerRenderMode() - [ ]
app.UseAntiforgery()is present in the middleware pipeline - [ ] If the app used `
, it has been replaced withAddCascadingAuthenticationState()` service registration - [ ] App builds and runs successfully on the target framework
Common Pitfalls
| Pitfall | Solution | |---------|----------| | Missing UseAntiforgery() middleware | AddRazorComponents registers antiforgery services, but the middleware must be explicitly added. Place app.UseAntiforgery() after UseAuthentication/UseAuthorization. Without it, form POST requests fail with 400 errors. | | Forgetting to replace blazor.server.js with blazor.web.js | The old script does not work with the Blazor Web App model. Replace all references to _framework/blazor.server.js with _framework/blazor.web.js. | | Not removing ` wrapper | The component wrapper does not work across render mode boundaries in Blazor Web Apps. Use builder.Services.AddCascadingAuthenticationState() instead. | | Leaving app.UseRouting() in the pipeline | Explicit UseRouting() is no longer needed and can interfere with endpoint routing. Remove it unless other middleware specifically requires it. | | Using InteractiveServer when prerendering was disabled | If the original app used render-mode="Server" (not "ServerPrerendered"), use new InteractiveServerRenderMode(prerender: false) to preserve the same behavior. Using InteractiveServer enables prerendering which can cause unexpected issues with components that depend on JS interop during initialization. | | Not migrating AddServerSideBlazor circuit options | If circuit options, hub options, or detailed error settings were configured, migrate them to AddInteractiveServerComponents(options => { ... }). Otherwise those settings are silently lost. | | UseAntiforgery() placed before authentication middleware | The antiforgery middleware must be placed after UseAuthentication and UseAuthorization. Placing it before causes antiforgery validation to run before the user identity is established. | | CSS isolation bundle link has wrong assembly name | If the ` tag referenced the old project name, update it to match the current assembly name. |
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
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: dotnet
- Source: dotnet/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.