11 minute read

You can find samples at https://github.com/stomp-js/samples/.

The STOMP Broker

Ensure your STOMP broker supports STOMP over WebSockets. While some brokers support this out of the box, others may require additional configuration or enabling a plugin.

Include stompjs

This npm package provides both a UMD build and ES modules. Web browsers can use the UMD build via a script tag. In Node.js, use ES module imports; CommonJS require is not supported.

Polyfills

Important: For Node.js and React Native, please check Polyfills.

In Web Browser

In Node.js, TypeScript, or ES6

This library is developed in TypeScript, and type definitions are included in the distribution.

You can import classes like the following:

import { Client, Message } from '@stomp/stompjs';

You can use these classes directly without prefixing them with StompJs..

There is no StompJs class/object to be imported.

Setting/getting options

All options can be set or read directly on the client instance:

// ES modules (Node.js/modern bundlers)
import { Client } from '@stomp/stompjs';

const client = new Client();
client.brokerURL = 'ws://localhost:15674/ws';

console.log(client.brokerURL);

If using the UMD bundle in a browser, create with the global namespace:

// UMD (browser via <script>)
const client = new StompJs.Client();
client.brokerURL = 'ws://localhost:15674/ws';
console.log(client.brokerURL);

You can also pass them as key–value pairs to the Client constructor or to Client#configure.

Create a STOMP client

STOMP JavaScript clients communicate with a STOMP server using a ws:// or wss:// URL.

The example below shows how to use this library when included via a script tag. When included via an ES module, no need to prefix the class names with StompJs.. It will be const client = new Client();.

const client = new StompJs.Client({
  brokerURL: 'ws://localhost:15674/ws',
  connectHeaders: {
    login: 'user',
    passcode: 'password',
  },
  debug: function (str) {
    console.log(str);
  },
  reconnectDelay: 5000,
  heartbeatIncoming: 4000,
  heartbeatOutgoing: 4000,
});

client.onConnect = function (frame) {
  // Do something; all subscriptions must be done in this callback.
  // This is needed because it runs after a (re)connect.
};

client.onStompError = function (frame) {
  // Invoked when the broker reports an error.
  // Bad login/passcode typically causes an error.
  // Compliant brokers set the `message` header with a brief message; the body may contain details.
  // Compliant brokers terminate the connection after any error.
  console.log('Broker reported error: ' + frame.headers['message']);
  console.log('Additional details: ' + frame.body);
};

client.activate();

To deactivate a client, call Client#deactivate. It stops reconnection attempts and disconnects any active connection.

// Prefer awaiting deactivation to ensure the client fully disconnects
await client.deactivate();

You can also request an immediate teardown by passing { force: true }. This skips both STOMP and WebSocket shutdown sequences and is useful if the underlying socket is stale and the browser is slow to close it.

// Forceful, immediate shutdown (skips STOMP and WebSocket close sequences)
await client.deactivate({ force: true });

Use a custom WebSocket (e.g., SockJS)

Instead of brokerURL, you may supply a factory that returns a WebSocket-like object. If both webSocketFactory and brokerURL are set, webSocketFactory is used.

import { Client } from '@stomp/stompjs';
import SockJS from 'sockjs-client';

const client = new Client({
  webSocketFactory: () => new SockJS('http://localhost:15674/stomp'),
});

client.activate();

Send messages

When the client is connected, it can send STOMP messages using the Client#publish method.

client.publish({destination: '/topic/general', body: 'Hello world'});

// There is an option to skip the Content-length header
client.publish({
  destination: '/topic/general',
  body: 'Hello world',
  skipContentLengthHeader: true,
});

// Additional headers
client.publish({
  destination: '/topic/general',
  body: 'Hello world',
  headers: {priority: '9'},
});

Starting with v5, sending binary messages is supported. To send a binary body, use the binaryBody parameter. It must be a Uint8Array.

const binaryData = generateBinaryData(); // This must be a Uint8Array
// Setting a content-type header is not mandatory, but is good practice
client.publish({
  destination: '/topic/special',
  binaryBody: binaryData,
  headers: {'content-type': 'application/octet-stream'},
});

Subscribe and receive messages

The STOMP client must subscribe to a destination to receive messages.

Use Client#subscribe. It takes two required arguments: destination (a string) and callback (a function that receives a message), plus an optional headers object for additional headers.

const subscription = client.subscribe('/queue/test', callback);

The subscribe method returns an object with an id (the client subscription ID) and an unsubscribe method to stop receiving from this destination.

Each time the broker sends a message, the client invokes the callback with a Message object.

const onMessage = function (message) {
  // Called when the client receives a STOMP message from the server
  if (message.body) {
    alert('Got message with body ' + message.body);
  } else {
    alert('Got empty message');
  }
};

const subscription = client.subscribe('/queue/test', onMessage);

You can pass optional headers when subscribing:

const headers = {ack: 'client'};
client.subscribe('/queue/test', message_callback, headers);

Here, the client specifies that it will handle message acknowledgments.

To stop receiving messages, call unsubscribe on the object returned by Client#subscribe.

const subscription = client.subscribe('/queue/test', onmessage);

// ... use the subscription ...

subscription.unsubscribe();

Binary messages

Prepare your broker

Not every broker supports binary messages out of the box. For example, RabbitMQ (see: https://www.rabbitmq.com/web-stomp.html) requires the following server configuration:

web_stomp.ws_frame = binary

Publishing binary messages

Use the binaryBody parameter of Client#publish to send binary data of type Uint8Array.

See Send messages for an example.

Receiving binary messages

The library does not infer whether incoming data is text or binary. Use Message#body to access the body as a string, or Message#binaryBody to access it as binary.

There is no generally accepted convention in STOMP (or messaging in general) to indicate binary messages. Producers and consumers should agree on a convention—for example, set a content-type header to indicate a binary payload.

// within message callback
if (message.headers['content-type'] === 'application/octet-stream') {
  // message is binary
  // call message.binaryBody
} else {
  // message is text
  // call message.body
}

JSON support

The body of a STOMP message must be a String or a Uint8Array. To send and receive JSON, use JSON.stringify() and JSON.parse() to convert between objects and strings.

const quote = {symbol: 'AAPL', value: 195.46};
client.publish({
  destination: '/topic/stocks',
  body: JSON.stringify(quote),
});

client.subscribe('/topic/stocks', function (message) {
  const quote = JSON.parse(message.body);
  alert(quote.symbol + ' is at ' + quote.value);
});

Acknowledgment

By default, the server automatically acknowledges STOMP messages before delivering them to the client.

Alternatively, the client can handle acknowledgments by subscribing with the ack header set to client or client-individual.

In that case, call Message#ack to inform the broker that the message has been processed.

const subscription = client.subscribe(
  '/queue/test',
  function (message) {
    // do something with the message
    // ...
    // and acknowledge it
    message.ack();
  },
  {ack: 'client'}
);

Message#ack accepts an optional headers object. For example, to acknowledge within a transaction and request a receipt:

const tx = client.begin();
message.ack({transaction: tx.id, receipt: 'my-receipt'});
tx.commit();

Use Message#nack with STOMP 1.1+ brokers to indicate that the client did not consume the message. It takes the same arguments as Message#ack.

Transactions

Messages can be sent and acknowledged within a transaction.

Start a transaction using Client#begin, which takes an optional transaction_id.

This returns an object with an id (the transaction ID) and two methods:

You can then send and acknowledge messages in the transaction by specifying a transaction identified by that id.

// start the transaction
const tx = client.begin();
// send the message in a transaction
client.publish({
  destination: '/queue/test',
  headers: {transaction: tx.id},
  body: 'message in a transaction',
});
// commit the transaction to effectively send the message
tx.commit();

If you forget to add the transaction header when calling Client#publish, the message will not be part of the transaction and will be sent immediately.

// start the transaction
const tx = client.begin();
// oops! send the message outside the transaction
client.publish({
  destination: '/queue/test',
  body: 'message in a transaction',
});
tx.abort(); // Too late! the message has been sent

Receipts

Some operations (SUBSCRIBE, SEND, BEGIN/COMMIT/ABORT, DISCONNECT) can be acknowledged by the broker via RECEIPT frames when you include a unique receipt header. Use Client#watchForReceipt to be notified when a given receipt arrives.

// Watch for a subscription receipt
const subReceipt = 'sub-' + crypto.randomUUID();
client.watchForReceipt(subReceipt, (frame) => {
  console.log('Subscribed, receipt:', frame.headers['receipt-id']);
});
client.subscribe('/queue/test', onMessage, { receipt: subReceipt });

// Watch for a publish receipt
const sendReceipt = 'send-' + crypto.randomUUID();
client.watchForReceipt(sendReceipt, () => {
  console.log('Message accepted by the broker.');
});
client.publish({
  destination: '/topic/general',
  body: 'Hello',
  headers: { receipt: sendReceipt },
});

If no watcher is active for an arriving receipt, Client#onUnhandledReceipt will be invoked.

Heart-beating

For STOMP 1.1 or higher, heart-beating is enabled by default. Options Client#heartbeatIncoming and Client#heartbeatOutgoing control heart-beating (default 10,000 ms). Set either to 0 to disable.

client.heartbeatOutgoing = 20000; // client will send heartbeats every 20000ms
client.heartbeatIncoming = 0; // client does not want to receive heartbeats from the server

Very small heartbeat intervals can increase server load; tune with care in production.

Heartbeat strategy

Control the scheduling strategy used for outgoing heartbeats with Client#heartbeatStrategy. Use 'interval' (default) or 'worker' (uses Web Workers when available). Using 'worker' may improve reliability when the page is in a background tab, where browsers often throttle or pause timers.

import { TickerStrategy } from '@stomp/stompjs';
client.heartbeatStrategy = TickerStrategy.Worker; // or TickerStrategy.Interval

Auto Reconnect

The client supports automatic reconnection after a connection failure. It is controlled by the Client#reconnectDelay option. The default is 5000 ms, meaning the client will attempt to reconnect 5 seconds after a drop.

// Add the following if you need automatic reconnect (delay is in milliseconds)
client.reconnectDelay = 300;

You can set reconnectDelay to a small value.

Reconnect with Exponential Backoff

// ES modules
import { ReconnectionTimeMode } from '@stomp/stompjs';

client.configure({
  reconnectTimeMode: ReconnectionTimeMode.EXPONENTIAL,
  reconnectDelay: 200, // It will wait 200, 400, 800 ms...
  maxReconnectDelay: 10000, // Optional: when provided, it will not wait more than this
});

When using exponential backoff, Client#maxReconnectDelay caps the wait time (default 15 minutes). Set it to 0 to disable the cap.

Connection timeout

Use Client#connectionTimeout to fail-fast if a connection is not established in time. When the timeout elapses without connecting, the underlying socket is closed and a reconnect is scheduled (if enabled).

client.connectionTimeout = 8000; // close and retry if not connected within 8 seconds

Debug

On a busy system, the volume of logs can be overwhelming. Therefore, debug messages are ignored by default.

Set the Client#debug property to a function (receives a String) to enable debug logging:

client.debug = function (str) {
  console.log(str);
};

Usually, headers of incoming and outgoing frames are logged. Set Client#logRawCommunication to log complete frames.

Callbacks

Lifecycle callbacks

  • Client#beforeConnect — invoked each time before connecting to the STOMP broker. You can modify connection parameters and other callbacks. On v6+, this callback may be async.
  • Client#onConnect — invoked each time the broker connects and the STOMP handshake completes.
  • Client#onDisconnect — invoked after each graceful disconnection. If the connection breaks due to an error or network failure, it is not called.
  • Client#onStompError — invoked when the broker reports an error.
  • Client#onWebSocketClose — invoked when the WebSocket closes. This is the most reliable way to detect that the connection terminated.
  • Client#onWebSocketError — invoked when the underlying WebSocket raises an error event.

Frame callbacks

  • Client#onUnhandledMessage — typically, brokers send messages corresponding to subscriptions. However, some brokers support concepts beyond standard STOMP (for example, RabbitMQ temporary queues). This callback is invoked if a message is received that is not linked to a subscription.
  • Client#onUnhandledReceipt — prefer Client#watchForReceipt. If a receipt arrives with no active watcher, this callback is invoked.
  • Client#onUnhandledFrame — invoked if the broker sends a non-standard STOMP frame.

State changes

Advanced options

These options are rarely needed; use only when you understand broker and environment behavior:

Updated: