How to use WebSockets in Angular

Using WebSockets in Angular enables real-time bidirectional communication between client and server for live updates and messaging. As the creator of CoreUI with over 12 years of Angular experience since 2014, I’ve implemented WebSocket connections in numerous real-time applications. Angular integrates with WebSockets using RxJS observables providing reactive streams for incoming messages and connection management. This approach enables real-time features like chat, notifications, live data feeds, and collaborative editing.

Create WebSocket service with RxJS Subject to manage connections and expose observable streams for real-time messaging.

Basic WebSocket service:

// websocket.service.ts
import { Injectable } from '@angular/core'
import { Observable, Subject, interval } from 'rxjs'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
import { retry, tap, delayWhen } from 'rxjs/operators'

@Injectable({
  providedIn: 'root'
})
export class WebSocketService {
  private socket$: WebSocketSubject<any>
  private messagesSubject$ = new Subject<any>()
  public messages$ = this.messagesSubject$.asObservable()

  constructor() {}

  connect(url: string): void {
    if (!this.socket$ || this.socket$.closed) {
      this.socket$ = webSocket({
        url: url,
        openObserver: {
          next: () => {
            console.log('WebSocket connected')
          }
        },
        closeObserver: {
          next: () => {
            console.log('WebSocket disconnected')
          }
        }
      })

      this.socket$
        .pipe(
          retry({
            delay: (error, retryCount) => {
              console.log(`Retry attempt ${retryCount}`)
              return interval(3000)
            }
          }),
          tap({
            error: (error) => console.error('WebSocket error:', error)
          })
        )
        .subscribe(
          (message) => this.messagesSubject$.next(message),
          (error) => console.error('WebSocket error:', error)
        )
    }
  }

  sendMessage(message: any): void {
    if (this.socket$) {
      this.socket$.next(message)
    }
  }

  disconnect(): void {
    if (this.socket$) {
      this.socket$.complete()
    }
  }
}

Chat component using WebSocket:

// chat.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import { WebSocketService } from './websocket.service'
import { Subject, takeUntil } from 'rxjs'

interface ChatMessage {
  user: string
  message: string
  timestamp: number
}

@Component({
  selector: 'app-chat',
  template: `
    <div class="chat-container">
      <div class="messages">
        <div *ngFor="let msg of messages" class="message">
          <strong>{{ msg.user }}:</strong> {{ msg.message }}
          <span class="timestamp">{{ msg.timestamp | date:'short' }}</span>
        </div>
      </div>
      <div class="input-area">
        <input
          [(ngModel)]="newMessage"
          (keyup.enter)="sendMessage()"
          placeholder="Type a message..."
        />
        <button (click)="sendMessage()">Send</button>
      </div>
    </div>
  `,
  styles: [`
    .chat-container {
      display: flex;
      flex-direction: column;
      height: 500px;
    }
    .messages {
      flex: 1;
      overflow-y: auto;
      padding: 1rem;
    }
    .message {
      margin-bottom: 0.5rem;
    }
    .input-area {
      display: flex;
      padding: 1rem;
      gap: 0.5rem;
    }
  `]
})
export class ChatComponent implements OnInit, OnDestroy {
  messages: ChatMessage[] = []
  newMessage = ''
  private destroy$ = new Subject<void>()

  constructor(private wsService: WebSocketService) {}

  ngOnInit(): void {
    this.wsService.connect('ws://localhost:8080/chat')

    this.wsService.messages$
      .pipe(takeUntil(this.destroy$))
      .subscribe((message: ChatMessage) => {
        this.messages.push(message)
      })
  }

  sendMessage(): void {
    if (this.newMessage.trim()) {
      const message: ChatMessage = {
        user: 'CurrentUser',
        message: this.newMessage,
        timestamp: Date.now()
      }
      this.wsService.sendMessage(message)
      this.newMessage = ''
    }
  }

  ngOnDestroy(): void {
    this.destroy$.next()
    this.destroy$.complete()
    this.wsService.disconnect()
  }
}

Advanced WebSocket service with reconnection:

// advanced-websocket.service.ts
import { Injectable } from '@angular/core'
import { Observable, Subject, timer, NEVER } from 'rxjs'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
import { retryWhen, tap, delayWhen, switchMap, catchError } from 'rxjs/operators'

export interface WebSocketMessage {
  type: string
  payload: any
}

@Injectable({
  providedIn: 'root'
})
export class AdvancedWebSocketService {
  private socket$: WebSocketSubject<WebSocketMessage>
  private messagesSubject$ = new Subject<WebSocketMessage>()
  private reconnectionDelay = 3000
  private maxReconnectionAttempts = 5
  private reconnectionAttempts = 0

  public messages$ = this.messagesSubject$.asObservable()
  public connectionStatus$ = new Subject<boolean>()

  connect(url: string): void {
    if (!this.socket$ || this.socket$.closed) {
      this.socket$ = this.createWebSocket(url)

      this.socket$
        .pipe(
          tap(() => {
            this.reconnectionAttempts = 0
            this.connectionStatus$.next(true)
          }),
          retryWhen((errors) =>
            errors.pipe(
              tap((error) => {
                console.error('WebSocket error:', error)
                this.connectionStatus$.next(false)
              }),
              delayWhen(() => {
                this.reconnectionAttempts++
                if (this.reconnectionAttempts >= this.maxReconnectionAttempts) {
                  console.error('Max reconnection attempts reached')
                  return NEVER
                }
                console.log(`Reconnecting in ${this.reconnectionDelay}ms...`)
                return timer(this.reconnectionDelay)
              })
            )
          ),
          catchError((error) => {
            console.error('Unrecoverable WebSocket error:', error)
            return NEVER
          })
        )
        .subscribe(
          (message) => this.messagesSubject$.next(message),
          (error) => console.error('WebSocket subscription error:', error)
        )
    }
  }

  private createWebSocket(url: string): WebSocketSubject<WebSocketMessage> {
    return webSocket({
      url: url,
      openObserver: {
        next: () => {
          console.log('WebSocket connection opened')
          this.connectionStatus$.next(true)
        }
      },
      closeObserver: {
        next: () => {
          console.log('WebSocket connection closed')
          this.connectionStatus$.next(false)
        }
      }
    })
  }

  sendMessage(type: string, payload: any): void {
    if (this.socket$ && !this.socket$.closed) {
      this.socket$.next({ type, payload })
    } else {
      console.warn('WebSocket is not connected')
    }
  }

  disconnect(): void {
    if (this.socket$) {
      this.socket$.complete()
      this.connectionStatus$.next(false)
    }
  }
}

Real-time notifications:

// notification.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import { AdvancedWebSocketService } from './advanced-websocket.service'
import { Subject, takeUntil } from 'rxjs'

interface Notification {
  id: string
  title: string
  message: string
  type: 'info' | 'warning' | 'error' | 'success'
}

@Component({
  selector: 'app-notifications',
  template: `
    <div class="notifications">
      <div class="status" [class.connected]="isConnected">
        {{ isConnected ? 'Connected' : 'Disconnected' }}
      </div>
      <div *ngFor="let notification of notifications"
           class="notification"
           [class]="notification.type">
        <h4>{{ notification.title }}</h4>
        <p>{{ notification.message }}</p>
        <button (click)="dismissNotification(notification.id)">×</button>
      </div>
    </div>
  `
})
export class NotificationComponent implements OnInit, OnDestroy {
  notifications: Notification[] = []
  isConnected = false
  private destroy$ = new Subject<void>()

  constructor(private wsService: AdvancedWebSocketService) {}

  ngOnInit(): void {
    this.wsService.connect('ws://localhost:8080/notifications')

    this.wsService.connectionStatus$
      .pipe(takeUntil(this.destroy$))
      .subscribe(status => {
        this.isConnected = status
      })

    this.wsService.messages$
      .pipe(takeUntil(this.destroy$))
      .subscribe((message) => {
        if (message.type === 'notification') {
          this.notifications.push(message.payload)
        }
      })
  }

  dismissNotification(id: string): void {
    this.notifications = this.notifications.filter(n => n.id !== id)
    this.wsService.sendMessage('dismiss', { id })
  }

  ngOnDestroy(): void {
    this.destroy$.next()
    this.destroy$.complete()
    this.wsService.disconnect()
  }
}

Live data dashboard:

// dashboard.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import { AdvancedWebSocketService } from './advanced-websocket.service'
import { Subject, takeUntil } from 'rxjs'

interface DashboardData {
  users: number
  revenue: number
  activeOrders: number
  timestamp: number
}

@Component({
  selector: 'app-dashboard',
  template: `
    <div class="dashboard">
      <div class="card">
        <h3>Active Users</h3>
        <p class="value">{{ data.users }}</p>
      </div>
      <div class="card">
        <h3>Revenue</h3>
        <p class="value">{{ data.revenue | currency }}</p>
      </div>
      <div class="card">
        <h3>Active Orders</h3>
        <p class="value">{{ data.activeOrders }}</p>
      </div>
      <div class="last-update">
        Last update: {{ data.timestamp | date:'medium' }}
      </div>
    </div>
  `
})
export class DashboardComponent implements OnInit, OnDestroy {
  data: DashboardData = {
    users: 0,
    revenue: 0,
    activeOrders: 0,
    timestamp: Date.now()
  }
  private destroy$ = new Subject<void>()

  constructor(private wsService: AdvancedWebSocketService) {}

  ngOnInit(): void {
    this.wsService.connect('ws://localhost:8080/dashboard')

    this.wsService.messages$
      .pipe(takeUntil(this.destroy$))
      .subscribe((message) => {
        if (message.type === 'dashboard-update') {
          this.data = message.payload
        }
      })
  }

  ngOnDestroy(): void {
    this.destroy$.next()
    this.destroy$.complete()
    this.wsService.disconnect()
  }
}

Authentication with WebSocket:

// authenticated-websocket.service.ts
import { Injectable } from '@angular/core'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
import { AuthService } from './auth.service'

@Injectable({
  providedIn: 'root'
})
export class AuthenticatedWebSocketService {
  private socket$: WebSocketSubject<any>

  constructor(private authService: AuthService) {}

  connect(url: string): void {
    const token = this.authService.getToken()

    this.socket$ = webSocket({
      url: `${url}?token=${token}`,
      openObserver: {
        next: () => {
          console.log('Authenticated WebSocket connected')
          // Send auth message
          this.socket$.next({
            type: 'auth',
            token: token
          })
        }
      }
    })

    this.socket$.subscribe(
      (message) => console.log('Received:', message),
      (error) => console.error('WebSocket error:', error)
    )
  }
}

Best Practice Note

Use RxJS webSocket for reactive WebSocket management. Implement automatic reconnection with exponential backoff. Clean up WebSocket connections in ngOnDestroy. Use Subject to broadcast messages to multiple subscribers. Handle connection status for UI feedback. Implement authentication via query params or initial message. Use typed interfaces for message structure. Handle errors gracefully with retry logic. This is how we implement WebSockets in CoreUI Angular applications—reactive streams with RxJS, automatic reconnection, proper cleanup, and typed messages ensuring reliable real-time communication.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author