signalr
This Claude Code skill provides guidance for implementing SignalR hubs, streaming, reconnection, and real-time patterns in ASP.NET Core applications. Use it when building chat, notification, or live-update features; debugging connection state or transport issues; or determining whether SignalR fits your scenario better than alternative transports.
git clone --depth 1 https://github.com/managedcode/dotnet-skills /tmp/signalr && cp -r /tmp/signalr/catalog/Frameworks/SignalR/skills/signalr ~/.claude/skills/signalrSKILL.md
# SignalR
## Trigger On
- building chat, notification, collaboration, or live-update features
- debugging hub lifetime, connection state, or transport issues
- deciding whether SignalR or another transport better fits the scenario
- implementing real-time broadcasting to groups of connected clients
- scaling SignalR across multiple servers
## Documentation
- [ASP.NET Core SignalR Overview](https://learn.microsoft.com/en-us/aspnet/core/signalr/introduction?view=aspnetcore-10.0)
- [SignalR Hubs](https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-10.0)
- [SignalR API Design Considerations](https://learn.microsoft.com/en-us/aspnet/core/signalr/api-design?view=aspnetcore-10.0)
- [SignalR Production Hosting and Scaling](https://learn.microsoft.com/en-us/aspnet/core/signalr/scale?view=aspnetcore-10.0)
- [SignalR Configuration](https://learn.microsoft.com/en-us/aspnet/core/signalr/configuration?view=aspnetcore-10.0)
### References
- [patterns.md](references/patterns.md) - Detailed hub patterns, streaming, groups, presence, and advanced messaging techniques
- [anti-patterns.md](references/anti-patterns.md) - Common SignalR mistakes and how to avoid them
## Workflow
1. Use SignalR for broadcast-style or connection-oriented real-time features; do not force gRPC into hub-style fan-out scenarios.
2. Model hub contracts intentionally and keep hub methods thin, delegating durable work elsewhere.
3. Plan for reconnection, backpressure, auth, and fan-out costs instead of treating real-time messaging as stateless request/response.
4. Use groups, presence, and connection metadata deliberately so scale-out behavior is understandable.
5. If Native AOT or trimming is in play, validate supported protocols and serialization choices explicitly.
6. Test connection behavior and failure modes, not just happy-path message delivery.
## Hub Patterns
### Strongly-Typed Hub (Recommended)
```csharp
// Define the client interface
public interface IChatClient
{
Task ReceiveMessage(string user, string message);
Task UserJoined(string user);
Task UserLeft(string user);
}
// Implement the strongly-typed hub
public class ChatHub : Hub<IChatClient>
{
public async Task SendMessage(string user, string message)
{
// Compiler checks client method calls
await Clients.All.ReceiveMessage(user, message);
}
public override async Task OnConnectedAsync()
{
await Clients.Others.UserJoined(Context.User?.Identity?.Name ?? "Anonymous");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await Clients.Others.UserLeft(Context.User?.Identity?.Name ?? "Anonymous");
await base.OnDisconnectedAsync(exception);
}
}
```
### Using Groups for Targeted Messaging
```csharp
public class NotificationHub : Hub<INotificationClient>
{
public async Task JoinGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).UserJoined(Context.User?.Identity?.Name);
}
public async Task LeaveGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
public async Task SendToGroup(string groupName, string message)
{
await Clients.Group(groupName).ReceiveNotification(message);
}
}
```
### Hub Method with Custom Object Parameters (API Versioning)
```csharp
// Use custom objects to avoid breaking changes
public class SendMessageRequest
{
public string Message { get; set; } = string.Empty;
public string? Recipient { get; set; } // Added later without breaking clients
public int? Priority { get; set; } // Added later without breaking clients
}
public class ChatHub : Hub<IChatClient>
{
public async Task SendMessage(SendMessageRequest request)
{
// Handle both old and new clients
if (request.Recipient != null)
{
await Clients.User(request.Recipient).ReceiveMessage(request.Message);
}
else
{
await Clients.All.ReceiveMessage(request.Message);
}
}
}
```
## Client Patterns
### JavaScript Client with Automatic Reconnection
```javascript
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.withAutomaticReconnect([0, 2000, 5000, 10000, 30000]) // Retry delays
.configureLogging(signalR.LogLevel.Information)
.build();
// Handle reconnection events
connection.onreconnecting(error => {
console.log("Reconnecting...", error);
updateUIForReconnecting();
});
connection.onreconnected(connectionId => {
console.log("Reconnected with ID:", connectionId);
// Rejoin groups - reconnection does not restore group membership
rejoinGroups();
updateUIForConnected();
});
connection.onclose(error => {
console.log("Connection closed", error);
updateUIForDisconnected();
});
async function start() {
try {
await connection.start();
console.log("SignalR Connected");
} catch (err) {
console.log(err);
setTimeout(start, 5000);
}
}
start();
```
### .NET Client with Reconnection
```csharp
var connection = new HubConnectionBuilder()
.WithUrl("https://localhost:5001/chatHub", options =>
{
options.AccessTokenProvider = () => Task.FromResult(GetAccessToken());
})
.WithAutomaticReconnect()
.Build();
connection.Reconnecting += error =>
{
_logger.LogWarning("Connection lost. Reconnecting: {Error}", error?.Message);
return Task.CompletedTask;
};
connection.Reconnected += connectionId =>
{
_logger.LogInformation("Reconnected with ID: {ConnectionId}", connectionId);
// Rejoin groups after reconnection
return RejoinGroupsAsync();
};
connection.Closed += async error =>
{
_logger.LogError("Connection closed: {Error}", error?.Message);
await Task.Delay(Random.Shared.Next(0, 5) * 1000);
awaiBuild, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on current .NET. USE FOR: working on ASP.NET Core apps, services, or middleware; changing auth, routing, configuration, hosting, or deployment behavior; deciding between ASP.NET Core sub-stacks. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Build, upgrade, and operate .NET Aspire 13.3.x application hosts with current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing, and Azure deployment patterns for distributed apps. USE FOR: Aspire.AppHost.Sdk, Aspire.Hosting.*, DistributedApplication.CreateBuilder, WithReference, WaitFor, AddProject, AddRedis, AddPostgres, aspire run, aspire init, aspire. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns. USE FOR: working on Azure Functions in .NET; migrating from the in-process model to the isolated worker model; adding Durable Functions, bindings, or host configuration. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices. USE FOR: building interactive web UIs with C# instead of JavaScript; choosing between Server, WebAssembly, or Auto render modes; designing component hierarchies and state. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. USE FOR: EF6 codebases; runtime versus ORM migration decisions; EDMX, code-first, ObjectContext, and legacy data-access review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET applications. USE FOR: DbContext, migrations, model configuration, EF queries, tracking, loading, performance, transactions, and EF6 migration decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Build, review, or migrate .NET MAUI applications across Android, iOS, macOS, and Windows with correct cross-platform UI, platform integration, and native packaging assumptions. USE FOR: working on cross-platform mobile or desktop UI in .NET MAUI; integrating device capabilities, navigation, or platform-specific code; migrating Xamarin.Forms or aligning. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
Use ML.NET to train, evaluate, or integrate machine-learning models into .NET applications with realistic data preparation, inference, and deployment expectations. USE FOR: ML.NET integration; local model training or retraining; inference pipelines, model loading, evaluation, and deployment review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.