Blazor Server: Real‑Time Stateful UI with SignalR – A Practical Chat Example
Learn how Blazor Server keeps component state on the server, syncs UI via SignalR, and what that means for bandwidth, latency, and scalability. A step‑by‑step chat app, trade‑offs, and verification checklist are included.
09 May 2026, 18:18 UTC

Problem: Why Keep UI State on the Server?
When building a chat or collaborative app, you want instant updates without reloading the page. Traditional JavaScript frameworks ship component logic to the client, which can bloat the initial payload and expose sensitive code. Blazor Server offers a different model: the component tree lives in the ASP.NET Core process, and the browser only receives UI diff patches over a SignalR connection.
Core Benefits of the Server‑Side Model
- Small Initial Download – The browser loads only the HTML shell, a small .NET runtime shim, and a minimal JavaScript SignalR client.
- Security – All component logic stays on the server; the client cannot tamper with it.
- Deterministic State – Server memory holds the component state; no need to rehydrate from local storage or cookies.
These advantages come at the cost of a persistent WebSocket connection and per‑interaction round‑trip latency.
Worked Example: A Minimal Chat Component
Below is a self‑contained example that demonstrates the model. The component keeps a list of messages in memory, adds new messages on submit, and triggers a UI re‑render using InvokeAsync(StateHasChanged).
@page "/chat"
@using System.Collections.Generic
<h3>Chat</h3>
<ul>
@foreach (var msg in Messages)
{
<li>@msg</li>
}
</ul>
<input @bind="NewMessage" placeholder="Type a message" />
<button @onclick="SendMessage">Send</button>
@code {
private List<string> Messages = new();
private string NewMessage;
private void SendMessage()
{
if (!string.IsNullOrWhiteSpace(NewMessage))
{
Messages.Add(NewMessage);
NewMessage = string.Empty;
// Force UI update on the client
InvokeAsync(StateHasChanged);
}
}
}
To set up the project:
- Run
dotnet new blazorserver -o BlazorChatin a terminal with the .NET 8 SDK installed. - Add the above
Chat.razorfile to thePagesfolder. - Update
App.razorto include a<RouteView>for the new page (the default template already supports this). - Start the app with
dotnet runand navigate tohttps://localhost:5001/chatin a browser.
Open DevTools and inspect the Network tab. You should see a persistent WebSocket connection to /signalr that remains open while you type and click the Send button. Each new message triggers a small JSON payload that the browser applies as a diff to the DOM.
Trade‑Offs & Limitations
- Server Resource Footprint – Each connected client consumes RAM to store its component state. A large user base can quickly exhaust memory.
- Latency Sensitivity – Every UI interaction requires a round‑trip to the server. On high‑latency networks, the user may feel sluggish.
- WebSocket Reliability – SignalR falls back to long‑polling if WebSockets are unavailable, but the experience degrades noticeably.
- Offline & Mobile – The app cannot function without an active connection; progressive‑web‑app features are limited.
When designing for high throughput or mobile‑first scenarios, consider Blazor WebAssembly or a hybrid approach where heavy logic runs client‑side.
Verification Checklist
| Step | What to Verify | How to Check |
|---|---|---|
| Initial Load Size | Confirm the payload is <1 MB | Check Network tab → DOMContentLoaded request size |
| SignalR Connection | Persistent WebSocket to /signalr | Look for WS entry staying open; no errors in console |
| Message Diff Size | Under 5 KB per message | Inspect the Message event payload in DevTools |
| State Preservation | Messages remain after a full page refresh | Reload page → verify messages still present |
| Connection Loss Handling | UI updates after reconnection | Simulate network drop → observe error in console, then restore |
Actionable Closing
If your application requires tight security, minimal client footprint, and can tolerate a single active connection per user, Blazor Server’s SignalR‑based real‑time model is a solid fit. Build a small prototype, run the above verification checklist, and measure the memory consumption of the server under realistic load. That data will guide whether you need to scale out with a dedicated SignalR hub or consider a client‑heavy alternative.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.