Blazor Interactivity
Blazor lets you ship a portal in one of three interactivity modes — Server, WebAssembly, or Auto. The mode picks how interactive components run (over a SignalR connection on the server, as compiled .NET in the browser, or both) and decides what gets generated in your project layout. Power Portals Pro runs correctly under all three; the differences are in deployment shape and project wiring, not in framework features.
The Three Modes
Server
Interactive components execute on the server. Each connected user holds an open SignalR connection back to the host, and every UI event round-trips to the server.
- Smallest client download. The browser receives plain HTML and a small SignalR loader — no .NET runtime ships to the client.
- Full server capabilities. Components inject any server-side service directly (Dataverse client, configuration, file system) without going through HTTP.
- Simplest deployment. One ASP.NET Core host, no separate WebAssembly bundle to publish.
Tradeoffs:
- Stateful host. Each user's circuit is pinned to a single server instance; horizontal scaling needs sticky sessions, and a host restart drops every active session.
- UI latency follows network latency. Every click, keystroke, and scroll round-trips to the server, so users on high-latency links feel the app as sluggish.
WebAssembly
Interactive components run in the browser as compiled .NET code. The server's job shrinks to serving static files and the framework's HTTP API endpoints under /api/auth/* and /api/*.
- Stateless server. The host scales horizontally with no sticky sessions, restarts cause no visible disruption, and per-user memory cost on the server is near zero.
- Snappy UI. Most interaction stays inside the browser — no SignalR round-trip per click — so the app feels instant once the bundle is loaded.
- Offline-capable. A loaded portal keeps rendering and validating against cached data even when the network drops, syncing back to the API when it returns.
Tradeoffs:
- Initial download. The .NET runtime, framework assemblies, and your portal's code ship to the browser on first load (compressed, often a couple MB). Subsequent visits load from the browser cache.
- API surface required. Anything the UI needs from the server has to be exposed as an HTTP endpoint. Power Portals Pro provides the data and auth endpoints out of the box; custom server logic needs its own controllers or minimal-API endpoints.
- Browser-side performance. CPU-heavy work (large grids, complex chart computations) runs at WebAssembly speed, which is fast but slower than native server-side .NET.
Auto
The first paint comes from the server (so the user sees content immediately) and the runtime transparently swaps to WebAssembly once the client bundle finishes downloading. Auto = Server first, WebAssembly afterwards, on the same routes.
- Best perceived load + best steady-state UX. Users get an instant first paint without waiting on the WASM bundle, then enjoy WebAssembly's snappy interactions once the runtime is cached in their browser.
- Single deployment. One host, one publish step — same as WebAssembly, with the Server runtime carried along for the prerender.
Tradeoffs:
- Most complex project layout. Both runtimes have to be wired up; components must work correctly under either renderer (no server-only services injected into shared components).
- Brief Server-rendered window. On first visit the page renders server-side until the WASM swap completes — Power Portals Pro's template hides this transition with a loading overlay so users don't see the intermediate state.
Tip
This documentation site runs in Auto mode. Open the browser dev tools' network tab on first load and you'll see the server-rendered HTML arrive first, then the WASM bundle stream in over a second or two — after which navigations stop hitting the server for HTML.
Which to Pick
Rough rules of thumb when there's no specific constraint forcing a choice:
- Pick Server if the portal has few concurrent users, runs inside a corporate network with stable connections, or your team is new to Blazor and wants the simplest mental model.
- Pick WebAssembly if the portal is customer-facing with high concurrency, your hosting bill scales with active sessions, or you need offline behavior. Accept the initial-download cost as a one-time hit per visitor.
- Pick Auto if you want the WebAssembly steady state without paying first-paint latency for the bundle download. Most production portals end up here.
Cheat Sheet
What changes between the three modes, file-by-file. Use this as the reference when comparing a generated project to one of the templates, or when chasing down a discrepancy after a manual switch.
Project Layout
All three modes generate a server host project AND a sibling .Client project. What differs is the .Client's SDK and whether it's actually compiled to a WebAssembly bundle.
- Server —
.ClientusesMicrosoft.NET.Sdk.Razor; it's a class library that holds shared components but isn't published as WASM. - WebAssembly —
.ClientusesMicrosoft.NET.Sdk.BlazorWebAssemblywith its ownProgram.cs, compiled to a downloadable bundle. - Auto — same as WebAssembly:
Microsoft.NET.Sdk.BlazorWebAssemblywith its ownProgram.cs.
.csproj Differences
The .Client project's SDK changes, and the WebAssembly-specific framework packages only appear when the host actually runs WASM:
<!-- Server-only: .Client is a Razor class library -->
<Project Sdk="Microsoft.NET.Sdk.Razor">
<!-- WebAssembly or Auto: .Client is a Blazor WebAssembly app -->
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
Additional package references when WebAssembly or Auto is active:
<!-- Server host (.csproj) — only when WebAssembly or Auto -->
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" />
<!-- .Client (.csproj) — only when WebAssembly or Auto -->
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="PowerPortalsPro.Web.Client" />
Server Program.cs — Service Registration
AddRazorComponents() picks up the appropriate render-mode component pairs based on which builder methods are chained:
// Server only
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
// WebAssembly only
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization();
// Auto (both)
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization();
Server Program.cs — Endpoint Mapping
MapRazorComponents<App>() chains the matching render-mode endpoints:
// Server only
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddAdditionalAssemblies(typeof(MyApp.Client._Imports).Assembly);
// WebAssembly only
app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(MyApp.Client._Imports).Assembly);
// Auto (both)
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(MyApp.Client._Imports).Assembly);
App.razor — Per-Route Render Policy
The PageRenderMode getter in App.razor decides which renderer each route uses. Server hosts return Server everywhere; WebAssembly and Auto hosts have to pin the /Account/* routes explicitly so the WASM-only IAuthService resolves:
// Server only — App.razor's PageRenderMode
return new InteractiveServerRenderMode();
// WebAssembly only — pin /Account/* to no-prerender so IAuthService resolves
if (HttpContext.Request.Path.StartsWithSegments("/Account"))
return new InteractiveWebAssemblyRenderMode(prerender: false);
return new InteractiveWebAssemblyRenderMode();
// Auto — same /Account/* pin, then Auto for everything else
if (HttpContext.Request.Path.StartsWithSegments("/Account"))
return new InteractiveWebAssemblyRenderMode(prerender: false);
return new InteractiveAutoRenderMode();
Why /Account/* is pinned
Auto's first paint runs under the Server renderer, but the Account pages'
IAuthServiceis only registered in the WASM client's DI graph. Without the explicit pin toInteractiveWebAssemblyRenderMode(prerender: false), an Auto host fails to resolve[Inject] IAuthServiceon the cold-session prerender.prerender: falseskips the server-side prerender step entirely so the page renders only once, on the WASM runtime.
.Client/Program.cs — Only When Interactive
Server-only hosts don't have a .Client/Program.cs at all (the .Client is a Razor class library). WebAssembly and Auto hosts include this file, which sets up the WASM runtime, registers the HttpClient with cookie credentials forwarding, registers Power Portals Pro's WASM client services, and pre-fetches cross-cutting localization at startup:
// .Client/Program.cs — only present for WebAssembly and Auto
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddFluentUIComponents();
builder.Services.AddSingleton<CookieCredentialsHandler>();
builder.Services.AddHttpClient("PowerPortalsPro", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<CookieCredentialsHandler>();
builder.Services.AddSingleton(sp =>
sp.GetRequiredService<IHttpClientFactory>().CreateClient("PowerPortalsPro"));
builder.Services.AddPowerPortalsProWebClient();
var app = builder.Build();
await app.UserPowerPortalsProWebClient(
LocalizationBaselines.Default.Concat(new[] { "app" }).ToArray());
await app.RunAsync();
Identity / Account Pages
Power Portals Pro ships two parallel sets of Account pages — one for each render context. Which set is in your project depends on the host's mode:
- Server — server-rendered Razor pages under
Components/Account/Pages/usingUserManagerdirectly. Form-post endpoints registered viaapp.MapAdditionalIdentityEndpoints(). - WebAssembly / Auto — interactive pages under
.Client/Pages/Account/using the framework'sIAuthServicewrapper. JSON endpoints registered viaapp.MapAuthEndpoints<PortalUser>()handle login, register, manage/*, and external-login completion asapplication/jsoncalls.
// Server only — form-post Identity endpoints used by the server-rendered Account pages
app.MapAdditionalIdentityEndpoints();
// WebAssembly or Auto — JSON /api/auth/* endpoints used by the .Client's IAuthService
app.MapAuthEndpoints<PortalUser>();
Next Steps
If you already have a project on one mode and want to convert to another, see the Switch Blazor Interactivity page for the file-by-file changes.
