$npx -y skills add managedcode/dotnet-skills --skill convert-blazor-server-to-webappGuides conversion of a pre-.NET 8 Blazor Server app into a .NET 8+ Blazor Web App. USE FOR: migrating apps that use AddServerSideBlazor and MapBlazorHub to the AddRazorComponents/MapRazorComponents model, converting _Host.cshtml to an App.razor root component, replacing blazor.se
| 1 | # Convert Blazor Server App to Blazor Web App |
| 2 | |
| 3 | 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. |
| 4 | |
| 5 | ## When to Use |
| 6 | |
| 7 | - Migrating a Blazor Server app from .NET 6 or .NET 7 to .NET 8+ |
| 8 | - App currently uses `AddServerSideBlazor()` and `MapBlazorHub()` in `Program.cs` (or `Startup.cs`) |
| 9 | - App uses `Pages/_Host.cshtml` (or `_Host.razor`) as the host page with Component Tag Helpers |
| 10 | - Want to adopt new Blazor Web App features while keeping interactive server rendering |
| 11 | |
| 12 | ## When Not to Use |
| 13 | |
| 14 | - **The app already uses `AddRazorComponents` and `MapRazorComponents`.** 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. |
| 15 | - Blazor WebAssembly or hosted Blazor WebAssembly app — these have a different migration path |
| 16 | - The app should stay on the legacy Blazor Server hosting model (just update TFM and packages) |
| 17 | - The app targets .NET Framework — it must be migrated to .NET first |
| 18 | |
| 19 | ## Inputs |
| 20 | |
| 21 | | Input | Required | Description | |
| 22 | |-------|----------|-------------| |
| 23 | | Blazor Server project | Yes | The `.csproj` and source files of the Blazor Server app | |
| 24 | | Target framework | Yes | .NET 8 or later (e.g., `net8.0`, `net9.0`, `net10.0`) | |
| 25 | | `Program.cs` or `Startup.cs` | Yes | The app's service and middleware configuration | |
| 26 | | `_Host.cshtml` location | Recommended | Usually `Pages/_Host.cshtml`; may be `_Host.razor` in some projects | |
| 27 | |
| 28 | ## Workflow |
| 29 | |
| 30 | > **Commit strategy:** Commit after each logical step so the migration is reviewable and bisectable. |
| 31 | |
| 32 | ### Step 1: Update the project file |
| 33 | |
| 34 | Update the `.csproj` file: |
| 35 | |
| 36 | 1. Change the Target Framework Moniker (TFM) to the target version: |
| 37 | ```xml |
| 38 | <TargetFramework>net8.0</TargetFramework> |
| 39 | ``` |
| 40 | 2. Update all `Microsoft.AspNetCore.*`, `Microsoft.EntityFrameworkCore.*`, `Microsoft.Extensions.*`, and `System.Net.Http.Json` package references to the matching version. |
| 41 | |
| 42 | For non-Blazor project file changes (nullable reference types, implicit usings, HTTP/3 support, etc.), see the [general ASP.NET Core migration guide](https://learn.microsoft.com/aspnet/core/migration/70-to-80). |
| 43 | |
| 44 | ### Step 2: Create `Routes.razor` from `App.razor` |
| 45 | |
| 46 | 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. |
| 47 | |
| 48 | 1. Create a new file `Routes.razor` in the project root. |
| 49 | 2. Move the entire content of `App.razor` into `Routes.razor`. |
| 50 | 3. If the content is wrapped in `<CascadingAuthenticationState>`, remove that wrapper (it will be replaced by a service in Step 5). |
| 51 | 4. Leave `App.razor` empty for the next step. |
| 52 | |
| 53 | The resulting `Routes.razor` should look similar to: |
| 54 | |
| 55 | ```razor |
| 56 | <Router AppAssembly="@typeof(Program).Assembly"> |
| 57 | <Found Context="routeData"> |
| 58 | <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> |
| 59 | <FocusOnNavigate RouteData="@routeData" Selector="h1" /> |
| 60 | </Found> |
| 61 | <NotFound> |
| 62 | <LayoutView Layout="@typeof(MainLayout)"> |
| 63 | <p>Sorry, there's nothing at this address.</p> |
| 64 | </LayoutView> |
| 65 | </NotFound> |
| 66 | </Router> |
| 67 | ``` |
| 68 | |
| 69 | If the app uses `<AuthorizeRouteView>` instead of `<RouteView>`, keep it — it works the same way in Blazor Web Apps. |
| 70 | |
| 71 | ### Step 3: Convert `_Host.cshtml` to `App.razor` |
| 72 | |
| 73 | 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: |
| 74 | |
| 75 | 1. **Remove Razor Page directives** — delete `@page "/"`, `@using Microsoft.AspNetCore.Components.Web`, `@namespace`, and `@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers`. |
| 76 | |
| 77 | 2. **Add component injection** — if using environment-conditional error UI, add: |