Use ASP.NET Core SignalR with a hosted Blazor WebAssembly app

By Daniel Roth and Luke Latham

This tutorial teaches the basics of building a real-time app using SignalR with Blazor WebAssembly. You learn how to:

  • Create a Blazor WebAssembly Hosted app project
  • Add the SignalR client library
  • Add a SignalR hub
  • Add SignalR services and an endpoint for the SignalR hub
  • Add Razor component code for chat

At the end of this tutorial, you'll have a working chat app.

View or download sample code (how to download)

Prerequisites

Create a hosted Blazor WebAssembly app project

Follow the guidance for your choice of tooling:

Note

Visual Studio 16.8 or later and .NET Core SDK 5.0.0 or later are required.

Note

Visual Studio 16.6 or later and .NET Core SDK 3.1.300 or later are required.

  1. Create a new project.

  2. Select Blazor App and select Next.

  3. Type BlazorSignalRApp in the Project name field. Confirm the Location entry is correct or provide a location for the project. Select Create.

  4. Choose the Blazor WebAssembly App template.

  5. Under Advanced, select the ASP.NET Core hosted check box.

  6. Select Create.

Add the SignalR client library

  1. In Solution Explorer, right-click the BlazorSignalRApp.Client project and select Manage NuGet Packages.

  2. In the Manage NuGet Packages dialog, confirm that the Package source is set to nuget.org.

  3. With Browse selected, type Microsoft.AspNetCore.SignalR.Client in the search box.

  4. In the search results, select the Microsoft.AspNetCore.SignalR.Client package and select Install.

  5. If the Preview Changes dialog appears, select OK.

  6. If the License Acceptance dialog appears, select I Accept if you agree with the license terms.

Add a SignalR hub

In the BlazorSignalRApp.Server project, create a Hubs (plural) folder and add the following ChatHub class (Hubs/ChatHub.cs):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;

namespace BlazorSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

Add services and an endpoint for the SignalR hub

  1. In the BlazorSignalRApp.Server project, open the Startup.cs file.

  2. Add the namespace for the ChatHub class to the top of the file:

    using BlazorSignalRApp.Server.Hubs;
    
  3. Add SignalR and Response Compression Middleware services to Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.AddControllersWithViews();
    services.AddResponseCompression(opts =>
    {
        opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
            new[] { "application/octet-stream" });
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR();
    services.AddControllersWithViews();
    services.AddResponseCompression(opts =>
    {
        opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
            new[] { "application/octet-stream" });
    });
}
  1. In Startup.Configure:

    • Use Response Compression Middleware at the top of the processing pipeline's configuration.
    • Between the endpoints for controllers and the client-side fallback, add an endpoint for the hub.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseResponseCompression();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseWebAssemblyDebugging();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHub<ChatHub>("/chathub");
        endpoints.MapFallbackToFile("index.html");
    });
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseResponseCompression();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseWebAssemblyDebugging();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseBlazorFrameworkFiles();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHub<ChatHub>("/chathub");
        endpoints.MapFallbackToFile("index.html");
    });
}

Add Razor component code for chat

  1. In the BlazorSignalRApp.Client project, open the Pages/Index.razor file.

  2. Replace the markup with the following code:

@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IAsyncDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    Task Send() =>
        hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;
        
    public async ValueTask DisposeAsync()
    {
        await hubConnection.DisposeAsync();
    }
}
@page "/"
@using Microsoft.AspNetCore.SignalR.Client
@inject NavigationManager NavigationManager
@implements IDisposable

<div class="form-group">
    <label>
        User:
        <input @bind="userInput" />
    </label>
</div>
<div class="form-group">
    <label>
        Message:
        <input @bind="messageInput" size="50" />
    </label>
</div>
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>

<hr>

<ul id="messagesList">
    @foreach (var message in messages)
    {
        <li>@message</li>
    }
</ul>

@code {
    private HubConnection hubConnection;
    private List<string> messages = new List<string>();
    private string userInput;
    private string messageInput;

    protected override async Task OnInitializedAsync()
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            messages.Add(encodedMsg);
            StateHasChanged();
        });

        await hubConnection.StartAsync();
    }

    Task Send() =>
        hubConnection.SendAsync("SendMessage", userInput, messageInput);

    public bool IsConnected =>
        hubConnection.State == HubConnectionState.Connected;
        
    public void Dispose()
    {
        _ = hubConnection.DisposeAsync();
    }
}

Run the app

  1. Follow the guidance for your tooling:
  1. In Solution Explorer, select the BlazorSignalRApp.Server project. Press F5 to run the app with debugging or Ctrl+F5 to run the app without debugging.

  2. Copy the URL from the address bar, open another browser instance or tab, and paste the URL in the address bar.

  3. Choose either browser, enter a name and message, and select the button to send the message. The name and message are displayed on both pages instantly:

    SignalR Blazor WebAssembly sample app open in two browser windows showing exchanged messages.

    Quotes: Star Trek VI: The Undiscovered Country ©1991 Paramount

Next steps

In this tutorial, you learned how to:

  • Create a Blazor WebAssembly Hosted app project
  • Add the SignalR client library
  • Add a SignalR hub
  • Add SignalR services and an endpoint for the SignalR hub
  • Add Razor component code for chat

To learn more about building Blazor apps, see the Blazor documentation:

Additional resources