Skip to content

Render Modes — Server, WASM, Auto

Key Points

  • Blazor (.NET 8+) unified Server + WASM under render modes. One project; per-component or per-page choice.
  • Static SSR — fastest first paint; no interactivity. Good for content pages.
  • Interactive ServerUI events go over SignalR to server; tiny WASM payload; needs persistent connection.
  • Interactive WebAssembly — full SPA in browser; slower initial load (download .NET runtime); offline capable.
  • Interactive Auto — starts as Server (instant), upgrades to WASM after download. Best UX; setup complexity.
  • Default: Static SSR for content; Auto for app-like pages; pick per page based on tradeoffs.

Concepts (deep dive)

The four modes

┌─────────────────┬──────────────┬────────────┬──────────────┐
│ Mode            │ First load   │ Latency    │ Online req'd │
├─────────────────┼──────────────┼────────────┼──────────────┤
│ Static SSR      │ Fastest      │ N/A (no UI)│ No           │
│ Interactive Srv │ Fast         │ Network RTT│ Yes (SignalR)│
│ Interactive WASM│ Slow         │ ~ms        │ No           │
│ Interactive Auto│ Fast → fast  │ Network→ms │ Initially Yes│
└─────────────────┴──────────────┴────────────┴──────────────┘

Setting render mode

@* Per-component *@
@rendermode InteractiveServer
@rendermode InteractiveWebAssembly
@rendermode InteractiveAuto
@* Static SSR is default — no @rendermode *@
@* Per-page *@
@page "/dashboard"
@rendermode @(new InteractiveServerRenderMode(prerender: false))

<Counter />

In Program.cs:

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddInteractiveWebAssemblyRenderMode();

Static SSR

@page "/about"
<h1>About Us</h1>

No interactivity (no @onclick handlers fire). Server renders HTML; browser displays. Forms submit via traditional HTTP POST.

Use for: content pages, landing pages, server-rendered forms.

Interactive Server

@page "/counter"
@rendermode InteractiveServer

<button @onclick="() => count++">Click @count</button>
@code { int count; }

UI events serialize to server over SignalR (WebSocket). Server runs handler; computes diff; sends back. Tiny client payload, server holds state.

Pros: - Tiny initial download. - Full .NET runtime, libraries. - Fast cold start.

Cons: - Per-user circuit (server memory). - Needs persistent connection. - Network RTT per interaction. - Doesn't scale to massive concurrent (need backplane).

Interactive WebAssembly

@page "/calculator"
@rendermode InteractiveWebAssembly

<button @onclick="() => result = a + b">+</button>
@code { int a, b, result; }

.NET runtime downloads to browser; runs in WASM. Standalone after load.

Pros: - Local execution; no server roundtrip. - Offline capable (PWA). - No server state.

Cons: - Initial download large (~5-10 MB compressed for runtime). - Slower cold start. - Fewer .NET features (no full BCL). - Browser sandbox limits.

Interactive Auto

@rendermode InteractiveAuto

First visit: Interactive Server (fast). Background: WASM downloads. Subsequent visits / after download: WASM.

Best UX — fast first load + local execution after.

Trickier: - Component runs on both server (initially) and WASM (later). - State must be transferable. - Both sides must work — every dep must work in browser.

Prerendering

@rendermode @(new InteractiveServerRenderMode(prerender: true))

Server renders HTML before sending. Improves SEO + first paint.

Pitfall: component runs twice — once during prerender, once when interactive. State must survive the boundary (PersistentComponentState).

Choosing

Content / SEO / static / minimal interactivity → Static SSR
LOB app, internal, online-required, server-resource-heavy → Interactive Server
Public app, offline support needed, big payload acceptable → Interactive WASM
Best UX (fast first load + local after) → Auto

Mixed modes in one app

@page "/"
@* Static SSR *@
<h1>Home</h1>
<NavLink href="/dashboard">Dashboard</NavLink>

@* Dashboard.razor uses Interactive Server *@

Per-page or per-component decisions.

State management

Mode State
Static SSR Per-request; URL/form
Interactive Server Per-circuit (per user/tab) on server
Interactive WASM In-memory in browser tab
Auto Both — must transfer

For cross-render-mode state, use PersistentComponentState:

[Inject] PersistentComponentState State { get; set; } = default!;

protected override Task OnInitializedAsync()
{
    if (State.TryTakeFromJson<MyData>("data", out var d) && d is not null)
        _data = d;
    else _data = await LoadAsync();

    State.RegisterOnPersisting(() =>
    {
        State.PersistAsJson("data", _data);
        return Task.CompletedTask;
    });
    return Task.CompletedTask;
}

State serialized into HTML; client picks up after WASM boots.

Authentication

AuthenticationState flows through both modes. Cookie auth typical for Server; PKCE OIDC for standalone WASM. See Auth in Blazor.

SignalR backplane (Interactive Server scale)

Multi-instance Interactive Server needs backplane (Redis or Azure SignalR). Otherwise, sticky LB.

WASM payload optimization

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <BlazorWebAssemblyEnableLinking>true</BlazorWebAssemblyEnableLinking>
</PropertyGroup>

Trimming removes unused code; AOT for fast execution but bigger size.

Common pitfalls

  • Mixing prerender + interactive WASM — component runs twice; state issues.
  • Heavy SQL calls in Interactive Server OnInitializedAsync — runs per circuit.
  • Calling browser APIs in prerenderIJSRuntime not available.
  • Auto without WASM-compatible deps — components fail when upgraded.

Code: correct vs wrong

❌ Wrong: per-component without thought

@rendermode InteractiveServer

Everywhere — server-resource heavy.

✅ Correct: Static SSR by default

@page "/"
<h1>Home</h1>
@* SSR default; opt into interactive only when needed *@

❌ Wrong: WASM lib calling System.IO

File.ReadAllText("/path");   // browser sandbox; fails

✅ Correct: API call

await Http.GetStringAsync("api/data");

Design patterns for this topic

Pattern 1 — "Static SSR by default; interactive opt-in"

  • Intent: minimum complexity per page.

Pattern 2 — "Auto for best UX"

  • Intent: fast first + local after.

Pattern 3 — "PersistentComponentState"

  • Intent: state across mode boundary.

Pattern 4 — "SignalR backplane for scale"

  • Intent: multi-instance Server.

Pattern 5 — "Trim + compress WASM"

  • Intent: smaller download.

Pros & cons / trade-offs

Mode Pros Cons
Static SSR Fastest; SEO No interactivity
Interactive Server Tiny payload Needs connection; server state
Interactive WASM Local; offline Big download
Auto Best UX Setup complexity

When to use / when to avoid

  • Use Static SSR for content.
  • Use Interactive Server for internal apps.
  • Use WASM for offline / no server state.
  • Use Auto for best UX.
  • Avoid mixing without clear strategy.

Interview Q&A

Q1. Four render modes? Static SSR, Interactive Server, Interactive WebAssembly, Interactive Auto.

Q2. Static SSR — interactivity? None. Forms via HTTP POST.

Q3. Interactive Server — how events flow? Over SignalR/WebSocket to server; server computes diff; sends back.

Q4. Interactive WASM cold start? Slow — runtime download (~5-10 MB compressed). Cached after.

Q5. Auto — what's the trick? Server initially (fast); WASM downloads in bg; future loads use WASM.

Q6. Prerender pitfalls? Component runs twice (prerender + interactive). State must survive — PersistentComponentState.

Q7. State across modes? PersistentComponentState transfers state from server prerender to WASM.

Q8. Multi-instance Interactive Server scaling? Sticky LB or SignalR backplane.

Q9. WASM trimming? PublishTrimmed true. Removes unused code.

Q10. When choose Auto over Server-only? When you want offline / lower server load AFTER initial load + can tolerate WASM download.

Q11. Per-component vs per-page mode? Both supported. Per-page common.

Q12. Static SSR alternatives? Razor Pages / MVC. Static SSR Blazor reuses component model.


Gotchas / common mistakes

  • ⚠️ Default Interactive Server everywhere — server load.
  • ⚠️ Browser-only APIs in prerender.
  • ⚠️ No PersistentComponentState — data refetched.
  • ⚠️ WASM-incompatible libs in Auto.
  • ⚠️ No backplane in multi-instance Server.

Further reading