SignalR Made Simple: Unlocking Real-Time Power in Your Application

🛠 What We’re Building

In this guide, we’ll walk through step by step how to implement a real-time team chat app using ASP.NET Core SignalR on the backend and Angular on the frontend.
By the end, you’ll have a working setup where users can join groups, send messages, and receive real-time updates.


📦 Step 1: Set Up the SignalR Hub

We start by creating a Hub in ASP.NET Core. This is the core piece that manages the real-time connections.

// File: Hubs/TeamChatHub.cs
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;

public class TeamChatHub : Hub
{
    // Join a group (e.g., Dev, QA, Sales)
    public async Task JoinGroup(string groupName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
        await Clients.Group(groupName).SendAsync("ReceiveMessage", "System", $"{Context.ConnectionId} joined {groupName}");
    }

    // Leave a group
    public async Task LeaveGroup(string groupName)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
        await Clients.Group(groupName).SendAsync("ReceiveMessage", "System", $"{Context.ConnectionId} left {groupName}");
    }

    // Send message to group
    public async Task SendMessageToGroup(string groupName, string user, string message)
    {
        await Clients.Group(groupName).SendAsync("ReceiveMessage", user, message);
    }
}

What’s happening?

  • Clients can join or leave named groups.

  • Messages sent to a group are broadcasted to all group members.

  • Explanation:

    • JoinGroup() uses:

        await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
      

      This adds the client’s connection (Context.ConnectionId) to a named group like Dev, QA, or Sales.

    • LeaveGroup() uses:

        await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
      

      This removes the client from the group.

    • SendMessageToGroup():
      Sends messages only to clients inside the specified group.

    • 💡 Tip:
      Here, you can also track which user joined by saving their ConnectionId, username, and group in your database.
      When removing, you can clear or update the session record in your database to show the user has left or gone offline.

      This helps you show online users or manage who’s active.

      Why?

      • Useful for showing online users.

      • Helps with managing group permissions or session timeouts.


🚀 Step 2: Create the SignalR Controller (Optional API)

We add a controller to send messages via HTTP API (for scenarios where backend services need to push updates).

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

namespace PracticeWebApplication.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SignalRController : ControllerBase
    {
        private readonly IHubContext<TeamChatHub> _hubContext;

        public SignalRController(IHubContext<TeamChatHub> hubContext)
        {
            _hubContext = hubContext;
        }

        [HttpPost("sendMessage")]
        public async Task<IActionResult> SendMessageToGroup([FromQuery] string groupName, [FromBody] MessageDto message)
        {
            await _hubContext.Clients.Group(groupName).SendAsync("ReceiveMessage", message.User, message.Content);
            return Ok("Message sent");
        }
    }

    public class MessageDto
    {
        public string User { get; set; }
        public string Content { get; set; }
    }
}

What’s happening?

  • Exposes an API endpoint POST /api/signalr/sendMessage to push messages to a group.

🔧 Step 3: Configure ASP.NET Core

In your Startup.cs (or Program.cs in .NET 6+), make sure to map the SignalR hub.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHub<TeamChatHub>("/teamChatHub");
});

Tip: Don’t forget to enable CORS if your frontend is on a different origin.


🌐 Step 4: Set Up Angular Frontend

Let’s switch to the Angular side and create the UI and SignalR connection.

🔌 Install SignalR Client

Run:

npm install @microsoft/signalr

📄 Angular Component HTML (UI)

<h2>Team Chat</h2>

<select #groupSelect>
  <option value="Dev">Dev</option>
  <option value="QA">QA</option>
  <option value="Sales">Sales</option>
</select>

<button (click)="joinGroup(groupSelect.value)" [disabled]="!connected">Join Group</button>
<button (click)="leaveGroup()">Leave Group</button>

<br /><br />

<input type="text" [(ngModel)]="userName" placeholder="Your Name" />
<input type="text" [(ngModel)]="messageText" placeholder="Enter message" />
<button (click)="sendMessage()" [disabled]="!connected || !currentGroup">Send</button>

<ul>
  <li *ngFor="let msg of messages">{{ msg }}</li>
</ul>

What’s happening?

  • Dropdown to pick a group.

  • Inputs for username and message.

  • Message list displaying chat history.


📄 Angular Component TypeScript (Logic)

import { Component, OnInit } from '@angular/core';
import * as signalR from '@microsoft/signalr';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})

export class AppComponent implements OnInit {
  connection!: signalR.HubConnection;
  currentGroup: string = '';
  userName: string = '';
  messageText: string = '';
  messages: string[] = [];
  connected: boolean = false;

  ngOnInit(): void {
    this.connection = new signalR.HubConnectionBuilder()
      .withUrl('https://localhost:44311/teamChatHub')
      .build();

    this.connection.on('ReceiveMessage', (user: string, message: string) => {
      this.messages.push(`${user}: ${message}`);
    });

    this.connection
      .start()
      .then(() => {
        console.log('Connected to SignalR');
        this.connected = true;
      })
      .catch(err => console.error('Connection error:', err.toString()));
  }

  joinGroup(group: string): void {
    this.currentGroup = group;
    this.connection.invoke('JoinGroup', group)
      .catch(err => console.error('JoinGroup error:', err.toString()));
  }

  leaveGroup(): void {
    if (this.currentGroup) {
      this.connection.invoke('LeaveGroup', this.currentGroup)
        .catch(err => console.error('LeaveGroup error:', err.toString()));
      this.currentGroup = '';
    }
  }

  sendMessage(): void {
    if (this.currentGroup && this.userName && this.messageText) {
      this.connection.invoke('SendMessageToGroup', this.currentGroup, this.userName, this.messageText)
        .catch(err => console.error('SendMessage error:', err.toString()));
      this.messageText = ''; // Clear input after send
    }
  }
}

What’s happening?

  • Establishes a SignalR connection on load.

  • Handles joining/leaving groups.

  • Listens for ReceiveMessage and updates the message list.


✅ Final Checklist

  • Backend SignalR Hub ready

  • Angular frontend wired up

  • Group join/leave + real-time messaging working

  • Optional API endpoint for backend push

  • Check your backend port — match it in Angular’s withUrl().

  • Enable CORS if frontend and backend run on different origins.

  • Test locally before deploying.


🎉 Conclusion

Congrats! You now have a real-time team chat app running with SignalR.
This pattern can be expanded to:

  • Live dashboards

  • Notifications

  • Collaborative tools

  • Multiplayer games

If you found this guide helpful, leave a like ❤️ or share it!

1
Subscribe to my newsletter

Read articles from Pranali Kulkarni directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pranali Kulkarni
Pranali Kulkarni