5 minute read

This step-by-step guide creates a new Angular application and demonstrates how to use rx-stomp.

While preparing this guide, Angular 13.2.0 and @stomp/rx-stomp 1.1.4 were used. The concepts apply to Angular 7+.

For the impatient, the final code from this tutorial is here: https://github.com/stomp-js/rx-stomp-angular

Prerequisites

Instructions

Create a new Angular application

Install the latest Angular CLI:

npm i @angular/cli -g

Scaffold the project:

ng new rx-stomp-angular --skip-install --defaults
cd rx-stomp-angular/
npm i

Run locally and open http://localhost:4200/:

ng serve

Keep this running. Angular CLI will rebuild and live-reload on changes.

Use your favorite IDE to edit code while the dev server runs.

Add @stomp/rx-stomp and inject RxStompService

Install the library:

npm i @stomp/rx-stomp

We’ll define configuration and wire it via Angular Dependency Injection when creating RxStompService.

Configuration

Create src/app/my-rx-stomp.config.ts:

import { RxStompConfig } from '@stomp/rx-stomp';

export const myRxStompConfig: RxStompConfig = {
  // Which server?
  brokerURL: 'ws://127.0.0.1:15674/ws',

  // Headers (typical keys: login, passcode, host)
  connectHeaders: {
    login: 'guest',
    passcode: 'guest',
  },

  // Heartbeats (ms). Set 0 to disable.
  heartbeatIncoming: 0,
  heartbeatOutgoing: 20000,

  // Reconnect delay (ms). Set 0 to disable.
  reconnectDelay: 200,

  // Console diagnostics (avoid in production)
  debug: (msg: string): void => {
    console.log(new Date(), msg);
  },
};

Works out-of-the-box with a default RabbitMQ STOMP-over-WebSocket setup; adjust for your broker.

The RxStompService

Create src/app/rx-stomp.service.ts:

import { Injectable } from '@angular/core';
import { RxStomp } from '@stomp/rx-stomp';

@Injectable({
  providedIn: 'root',
})
export class RxStompService extends RxStomp {
  constructor() {
    super();
  }
}

Factory to create and initialize RxStompService

Create src/app/rx-stomp-service-factory.ts:

import { RxStompService } from './rx-stomp.service';
import { myRxStompConfig } from './my-rx-stomp.config';

export function rxStompServiceFactory() {
  const rxStomp = new RxStompService();
  rxStomp.configure(myRxStompConfig);
  rxStomp.activate();
  return rxStomp;
}

Angular DI setup

In src/app/app.module.ts, add a provider:

providers: [
  {
    provide: RxStompService,
    useFactory: rxStompServiceFactory,
  },
];

And import near the top:

import { RxStompService } from './rx-stomp.service';
import { rxStompServiceFactory } from './rx-stomp-service-factory';

Messages

We’ll build a component that:

  • Sends a message on button click.
  • Subscribes and lists received messages.

Skeleton

Create the Messages component:

ng generate component messages

Set src/app/messages/messages.component.html:

<div id="messages">
  <button class="btn btn-primary">Send Test Message</button>
  <h2>Received messages</h2>
  <ol>
    <!-- list will be populated via Angular binding -->
    <li class="message">message</li>
  </ol>
</div>

Add the component to the main UI (src/app/app.component.html):

<div style="text-align:center">
  <h1>Welcome to !</h1>
</div>
<app-messages></app-messages>

Check the browser to confirm the updated UI. If the page is blank, check terminal output from ng serve and the browser console for errors.

Sending messages

Inject RxStompService into MessagesComponent (src/app/messages/messages.component.ts):

constructor(private rxStompService: RxStompService) {}

Add a click handler:

onSendMessage() {
  const message = `Message generated at ${new Date()}`;
  this.rxStompService.publish({ destination: '/topic/demo', body: message });
}

See RxStomp#publish for options like headers, receipts, and binary payloads.

src/app/messages/messages.component.ts (so far):

import { Component, OnInit } from '@angular/core';
import { RxStompService } from '../rx-stomp.service';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit {
  constructor(private rxStompService: RxStompService) {}

  ngOnInit(): void {}

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}

Wire the button in the template with (click)="onSendMessage()":

<div id="messages">
  <button class="btn btn-primary" (click)="onSendMessage()">
    Send Test Message
  </button>
  <h2>Received messages</h2>
  <ol>
      <!-- list will be populated via Angular binding -->
    <li class="message">message</li>
  </ol>
</div>

Open the browser console. Clicking “Send Test Message” should log frame activity from the debug logger.

Receiving messages

Use RxStomp#watch to subscribe to /topic/demo:

ngOnInit() {
  this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
    this.receivedMessages.push(message.body);
  });
}

Import Message from @stomp/stompjs and declare the array. Then bind the list with ngFor. A few other modules also expose Message classes, so you need to be careful.

If you are coming from @stomp/stompjs, please notice that you do not need to subscribe within the callback of stomp getting connected. This library internally ensures that actual subscription happens when the broker is actually connected. It also keeps track of broker re-connections and automatically resubscribes.

Update src/app/messages/messages.component.html to render messages:

<li class="message" *ngFor="let message of receivedMessages"></li>

Complete files:

import { Component, OnInit } from '@angular/core';
import { RxStompService } from '../rx-stomp.service';
import { Message } from '@stomp/stompjs';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit {
  receivedMessages: string[] = [];

  constructor(private rxStompService: RxStompService) {}

  ngOnInit(): void {
    this.rxStompService.watch('/topic/demo').subscribe((message: Message) => {
      this.receivedMessages.push(message.body);
    });
  }

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}
<div id="messages">
  <button class="btn btn-primary" (click)="onSendMessage()">
    Send Test Message
  </button>
  <h2>Received messages</h2>
  <ol>
    <li class="message" *ngFor="let message of receivedMessages">
      
    </li>
  </ol>
</div>

Try sending a few messages, then open another browser tab/window: both should receive messages.

Stopping the watch

Unsubscribe when the component is destroyed to avoid leaks:

  • Implement OnDestroy.
  • Store the Subscription returned by subscribe.
  • Call unsubscribe() in ngOnDestroy.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { RxStompService } from '../rx-stomp.service';
import { Message } from '@stomp/stompjs';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-messages',
  templateUrl: './messages.component.html',
  styleUrls: ['./messages.component.css'],
})
export class MessagesComponent implements OnInit, OnDestroy {
  receivedMessages: string[] = [];
  private topicSubscription!: Subscription;

  constructor(private rxStompService: RxStompService) {}

  ngOnInit() {
    this.topicSubscription = this.rxStompService
      .watch('/topic/demo')
      .subscribe((message: Message) => {
        this.receivedMessages.push(message.body);
      });
  }

  ngOnDestroy() {
    this.topicSubscription.unsubscribe();
  }

  onSendMessage() {
    const message = `Message generated at ${new Date()}`;
    this.rxStompService.publish({ destination: '/topic/demo', body: message });
  }
}

Tip: You can also use the RxJS takeUntil pattern with a Subject if you prefer that style.

Troubleshooting and tips

  • Connection URL: Ensure brokerURL points to your broker’s STOMP-over-WebSocket endpoint (e.g., RabbitMQ default: ws://localhost:15674/ws).
  • CORS/HTTPS: When serving the Angular app over HTTPS, use wss:// and ensure certificates are valid.
  • Heartbeats: Some proxies or brokers may need heartbeats tuned; start with incoming: 0, outgoing: 20000 and adjust.
  • Debug logs: Keep debug enabled during development to see connection and frame flow.
  • Auth tokens: For token-based auth, see the token authentication FAQ and set headers in connectHeaders.

Where next

Updated: