Overview
Step-by-step: live-updating dashboards using SignalR Hub groups, Blazor Server, and efficient diff rendering.
Architecture Overview
Real-time dashboards require a persistent connection between server and client. SignalR abstracts WebSockets, Server-Sent Events, and Long Polling behind a clean Hub API, automatically selecting the best transport.
In Blazor Server, this is particularly elegant: your C# component code already runs on the server, and Blazor's rendering pipeline efficiently sends only DOM diffs to the client—no manual JSON serialisation required.
// Hub definition
public class DashboardHub : Hub
{
public async Task JoinGroup(string dashboardId)
=> await Groups.AddToGroupAsync(Context.ConnectionId, dashboardId);
public async Task LeaveGroup(string dashboardId)
=> await Groups.RemoveFromGroupAsync(Context.ConnectionId, dashboardId);
}Hub Groups for Multi-Tenant Dashboards
Hub groups allow you to target messages to specific sets of connected clients. For multi-tenant SaaS dashboards, each tenant gets their own group—ensuring data isolation without maintaining complex connection maps.
We used this pattern in ARC ERP to let managers across different business units see their own real-time metrics without any cross-contamination.
- Create a group per tenant/dashboard
- Join on component OnInitializedAsync
- Leave on component disposal
- Broadcast using IHubContext<T> from services
Efficient State Management
The key to performant real-time Blazor is minimising re-renders. Use StateHasChanged() sparingly and batch updates using InvokeAsync to marshal back to the UI thread. Consider using records for immutable state—Blazor's differ handles them efficiently.
// Correct pattern
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/dashboardhub"))
.WithAutomaticReconnect()
.Build();
_hubConnection.On<DashboardData>("Update", data =>
InvokeAsync(() => { _data = data; StateHasChanged(); }));
await _hubConnection.StartAsync();
}Key Takeaways
- Use Hub groups for multi-tenant isolation
- Always marshal UI updates via InvokeAsync
- WithAutomaticReconnect() for production resilience
- Dispose Hub connections in IAsyncDisposable
- Batch state updates to minimise re-renders
Saurav Rai
Founder & Lead Architect, Omni Stack
7+ years building enterprise .NET and cloud applications for clients across Australia, USA, and the Middle East. Passionate about clean architecture, developer experience, and shipping fast.