I’ll cover the two most common scenarios:
Blazor WebAssembly (hosted) — a WASM client + ASP.NET Core server that exposes a Hub (typical chat/notifications scenario).
Blazor Server — explanation + the recommended patterns for server-side apps (because Blazor Server already uses SignalR under the hood).
I’ll include full code snippets (Hub, Program.cs, Blazor component), CORS/auth tips, reconnection, groups, streaming, and a quick background-service example for server push.
Quick comparison — which to pick
Blazor WebAssembly (hosted) — choose when you have a separate server that needs to push to browser clients (classic SignalR use).
Blazor Server — you already have real-time updates via Blazor circuits; for server-originated events use a shared notification service or a hub if you need non-Blazor clients to receive messages.
Prerequisites
.NET 6/7/8+ SDK installed (examples below use the minimal hosting model).
Basic knowledge of C# + Blazor.
CLI commands use
dotnet.
A. Blazor WebAssembly (hosted) — example: simple Chat
1) Create a hosted Blazor WebAssembly solution
dotnet new blazorwasm --hosted -o BlazorSignalRApp
cd BlazorSignalRApp
This creates three projects: Client , Server , Shared .
2) Add packages
Server needs SignalR server (usually already present in ASP.NET Core):
builder.Services.AddSignalR();Client needs SignalR client package:
cd Client
dotnet add package Microsoft.AspNetCore.SignalR.Client
3) Create the Hub (Server project) Server/Hubs/ChatHub.cs
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// Broadcast to all connected clients
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
4) Wire up SignalR & CORS (Server Program.cs) Server/Program.cs (minimal example)
var builder = WebApplication.CreateBuilder(args);
// Allow the client origin (adjust ports/urls as needed)
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", policy =>
policy.WithOrigins("https://localhost:5001") // client origin(s)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.MapRazorPages();
app.MapControllers();
app.MapHub<ChatHub>("/chathub"); // hub endpoint
app.MapFallbackToFile("index.html");
app.Run();
Important: If client/server run on different origins, AllowCredentials() and WithOrigins(...) are required and you must not use AllowAnyOrigin() with credentials.
5) Create the Blazor client UI & connect (Client project) Client/Pages/Chat.razor
@page "/chat"
@using Microsoft.AspNetCore.SignalR.Client
@using Microsoft.AspNetCore.Components.Web
@inject NavigationManager Navigation
<h3>SignalR Chat</h3>
<input placeholder="Your name" @bind="user" />
<input placeholder="Type a message" @bind="message" @onkeydown="HandleKeyDown"/>
<button @onclick="Send">Send</button>
<ul>
@foreach (var m in messages)
{
<li>@m</li>
}
</ul>
@code {
private HubConnection? hubConnection;
private string user = "Guest";
private string message = "";
private List<string> messages = new();
protected override async Task OnInitializedAsync()
{
// Build connection to the hub path on the *server* project
hubConnection = new HubConnectionBuilder()
.WithUrl(Navigation.ToAbsoluteUri("/chathub")) // URL maps to Server/MapHub
.WithAutomaticReconnect()
.Build();
hubConnection.On<string, string>("ReceiveMessage", (userFromServer, messageFromServer) =>
{
messages.Add($"{userFromServer}: {messageFromServer}");
InvokeAsync(StateHasChanged);
});
await hubConnection.StartAsync();
}
private async Task Send()
{
if (string.IsNullOrWhiteSpace(message) || hubConnection is null) return;
await hubConnection.InvokeAsync("SendMessage", user, message);
message = "";
}
private async Task HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Enter") await Send();
}
public async ValueTask DisposeAsync()
{
if (hubConnection != null)
{
await hubConnection.DisposeAsync();
}
}
}
Join the conversation! Your thoughts help the community grow.