Switch Blazor Interactivity

Switching an existing Power Portals Pro project from one interactivity mode to another is a series of edits to Program.cs, App.razor, the .Client project, and (when adding interactivity) the Account pages. The framework itself does not need any changes — only the host wiring does. The steps below cover the common transitions.

Tip

If your customizations live in a small number of pages, the lowest-friction path is to scaffold a fresh project in the target mode and copy your customizations across. Comparing your current host to a freshly generated reference is also useful when chasing down a discrepancy after a manual switch.

Server → WebAssembly or Auto

These steps add WebAssembly to a Server-only host. The same edits apply whether you're targeting WebAssembly-only or Auto — the only differences are which builder methods you chain in Program.cs and which render mode App.razor returns. Both are called out per step.

1. Convert the .Client project to a WebAssembly app

Open .Client/MyApp.Client.csproj and change the SDK from Microsoft.NET.Sdk.Razor to Microsoft.NET.Sdk.BlazorWebAssembly. Add the WebAssembly framework and Power Portals Pro client packages:

Add the WebAssembly server-side hosting package to the server host's .csproj as well — it provides the middleware that serves the WASM bundle:

2. Register interactive WebAssembly components on the server

In the server Program.cs, replace the component registration with the matching builder calls. AddAuthenticationStateSerialization() serializes the authenticated user across the Server→WASM boundary so the cascading AuthenticationState is consistent on both runtimes:

3. Map the WebAssembly render mode

Update app.MapRazorComponents<App>() to chain the matching render-mode endpoints. The AddAdditionalAssemblies call already points at the .Client assembly's _Imports in the templates, so it doesn't change here:

4. Update App.razor's PageRenderMode

In App.razor's PageRenderMode getter, return the new render mode. Pin /Account/* to InteractiveWebAssemblyRenderMode(prerender: false) first, then return the default for everything else:

The Account-route pin is required because the framework's IAuthService is only registered in the WASM client's DI graph. Without the pin, Auto's server-side prerender fails to resolve [Inject] IAuthService on a cold session.

5. Add .Client/Program.cs

Create a Program.cs in the .Client project. This sets up the WebAssembly host, registers the HttpClient with the cookie-forwarding handler (so authenticated calls from the WASM client to /api/* see the same auth cookie as the browser), and calls AddPowerPortalsProWebClient to register the WASM-side framework services. The UserPowerPortalsProWebClient call pre-fetches the cross-cutting localization strings at startup so the first paint doesn't flash key-as-fallback text:

6. Switch from form-post to JSON Auth endpoints

Server hosts use MapAdditionalIdentityEndpoints() for form-post Identity pages. WebAssembly and Auto hosts use MapAuthEndpoints<TUser>() instead — the .Client's IAuthService wrapper calls these JSON endpoints under /api/auth/*:

When the host runs both Server and WASM Account pages (uncommon), both endpoint registrations can coexist. The default templates pick one or the other based on the interactivity mode.

7. Move the Account pages to the .Client project

Power Portals Pro ships two parallel sets of Account pages, one for each render context:

8. Optional — scoped exception handler for /api/*

When WebAssembly is in play, exceptions thrown from /api/* need to round-trip to the WASM client as RFC 9457 problem+json so the client-side PowerPortalsProService can rehydrate the original CLR type. The templates wire this with a scoped UseExceptionHandler for /api/* only — Developer Exception Page still handles server-rendered page errors elsewhere:

WebAssembly or Auto → Server

Reverse the steps above:

Auto ↔ WebAssembly

The cheapest switch — the project layout, packages, and Account pages are identical between the two. Only three things change:

Verifying the Switch

After making the changes:

Note

Watch out for service-lifetime mismatches when moving services between the server and the WASM client. The WASM host runs as a single scope per session, but the framework's caching services are registered as singletons — if you register your own service as Transient on the WASM side, per-instance state silently resets every resolve. Use Singleton on the WASM side for any service that holds mutable state.