@stomp/stompjs, sockjs-client, and stompjs address real-time communication in web applications, but they operate at different layers of the network stack. @stomp/stompjs is the modern, actively maintained client for the STOMP (Simple Text Oriented Messaging Protocol), enabling structured message exchange over WebSocket or other transports. sockjs-client provides a browser-side transport layer that mimics the WebSocket API but offers fallback mechanisms (like XHR streaming) for environments where raw WebSockets are blocked or unstable. stompjs refers to the legacy, unscoped version of the STOMP client, which is largely superseded by the scoped @stomp/stompjs package. Together, these tools allow developers to build robust chat systems, live dashboards, and collaborative features, though choosing the right combination depends on server capabilities and browser compatibility requirements.
Building real-time features like chat, notifications, or live trading dashboards requires a solid understanding of how messages move between client and server. The packages @stomp/stompjs, sockjs-client, and stompjs often appear together in this space, but they solve different problems. @stomp/stompjs and stompjs handle the application-level messaging protocol (STOMP), while sockjs-client handles the underlying network transport. Let's break down how they differ and how to architect your connection layer correctly.
The most common confusion is treating STOMP and SockJS as interchangeable. They are not. STOMP is a messaging protocol (like HTTP) that defines how messages are framed and routed. SockJS is a transport layer (like TCP) that defines how bytes move over the network.
@stomp/stompjs implements the STOMP protocol. It expects a transport layer to send raw frames.
// @stomp/stompjs: Requires a transport (WebSocket or SockJS)
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'wss://example.com/stomp',
connectHeaders: { login: 'user', passcode: 'secret' },
onConnect: () => console.log('Connected via STOMP')
});
client.activate();
sockjs-client implements the SockJS transport protocol. It does not understand STOMP frames on its own.
// sockjs-client: Pure transport, no STOMP logic
import SockJS from 'sockjs-client';
const socket = new SockJS('https://example.com/sockjs');
socket.onopen = () => console.log('Transport Open');
socket.send('Raw string data'); // No STOMP framing
stompjs (legacy) also implements STOMP but uses an older API style and lacks modern maintenance.
// stompjs (legacy): Older API style
import Stomp from 'stompjs';
const client = Stomp.over(new WebSocket('wss://example.com/stomp'));
client.connect('user', 'secret', () => console.log('Connected'));
Choosing the right package starts with checking if it is still alive. The ecosystem has shifted significantly toward the scoped npm package.
@stomp/stompjs is the current standard. It is rewritten in TypeScript, supports module imports, and receives regular updates for bug fixes and STOMP 1.2 compliance.
// @stomp/stompjs: Modern ES6 imports and class-based API
import { Client, Frame } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'wss://example.com/stomp',
onMessage: (frame) => console.log(frame.body)
});
stompjs (unscoped) is effectively deprecated for new work. While it might still function, it lacks the architectural improvements of its successor and may not handle edge cases in modern browsers correctly.
// stompjs (legacy): Callback-heavy API
import Stomp from 'stompjs';
// No class instantiation, uses global-like object
Stomp.debug = true;
const client = Stomp.client('wss://example.com/stomp');
sockjs-client remains stable. Since it solves a specific compatibility problem (WebSocket fallbacks), its API surface hasn't needed major changes. It is safe to use if your server demands it.
// sockjs-client: Stable event emitter API
import SockJS from 'sockjs-client';
const socket = new SockJS('/sockjs');
socket.onmessage = (e) => console.log(e.data);
One of the strongest features of @stomp/stompjs is its ability to swap transports. You can use native WebSockets for performance or sockjs-client for compatibility without changing your STOMP logic.
@stomp/stompjs with Native WebSocket (Recommended for modern browsers)
// @stomp/stompjs: Direct WebSocket transport
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'wss://example.com/stomp',
// Uses native WebSocket by default
});
@stomp/stompjs with SockJS (For restrictive networks)
// @stomp/stompjs: Injecting SockJS as transport
import { Client } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
const client = new Client({
brokerURL: 'https://example.com/sockjs',
webSocketFactory: () => new SockJS('https://example.com/sockjs'),
});
sockjs-client Standalone (Without STOMP)
// sockjs-client: Raw transport only
import SockJS from 'sockjs-client';
// You must handle your own message framing
const socket = new SockJS('/sockjs');
socket.send(JSON.stringify({ type: 'EVENT', payload: {} }));
Real-world networks are unstable. Handling disconnects gracefully is critical for user experience. The libraries handle this differently.
@stomp/stompjs has built-in reconnection logic. You configure it once, and it handles the retry loop automatically.
// @stomp/stompjs: Built-in reconnection config
const client = new Client({
brokerURL: 'wss://example.com/stomp',
reconnectDelay: 5000, // Retry every 5 seconds
onDisconnect: () => console.log('Disconnected, retrying...')
});
stompjs (legacy) requires manual reconnection handling. You must write the retry logic yourself inside the disconnect callback.
// stompjs (legacy): Manual reconnection
const client = Stomp.over(ws);
client.onclose = () => {
setTimeout(() => connectAgain(), 5000); // Custom logic needed
};
sockjs-client has its own reconnection for the transport layer, but it doesn't know about application-level heartbeats.
// sockjs-client: Transport level reconnect
const socket = new SockJS('/sockjs');
socket.onclose = () => console.log('Transport closed');
// Does not handle STOMP heartbeat timeouts
How you listen for messages defines the structure of your frontend code. STOMP provides a topic-based subscription model, while raw SockJS is just a stream of data.
@stomp/stompjs uses subscription IDs and headers for robust message routing.
// @stomp/stompjs: Structured subscription
const subscription = client.subscribe('/topic/messages', (message) => {
console.log(`Received: ${message.body}`);
});
// Later: subscription.unsubscribe();
stompjs (legacy) uses a similar model but with a callback-based return value.
// stompjs (legacy): Callback subscription
const sub = client.subscribe('/topic/messages', (message) => {
console.log(`Received: ${message.body}`);
});
sockjs-client has no concept of topics. You receive every message sent to the connection and must filter manually.
// sockjs-client: Manual message filtering
socket.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.destination === '/topic/messages') {
console.log(`Received: ${data.body}`);
}
};
Authentication in real-time apps often happens during the connection handshake. STOMP supports headers for this, while raw SockJS relies on cookies or URL params.
@stomp/stompjs allows secure header injection during connect.
// @stomp/stompjs: Secure connect headers
const client = new Client({
brokerURL: 'wss://example.com/stomp',
connectHeaders: {
Authorization: `Bearer ${accessToken}`
}
});
stompjs (legacy) supports headers but with a slightly different argument signature.
// stompjs (legacy): Connect with headers
client.connect({ login: 'user', passcode: 'secret' }, onConnect);
sockjs-client does not support custom headers on the WebSocket handshake due to browser limitations. You must use cookies or query parameters.
// sockjs-client: No custom headers on handshake
// Auth must be via Cookie or URL query param
const socket = new SockJS('https://example.com/sockjs?token=xyz');
| Feature | @stomp/stompjs | sockjs-client | stompjs (Legacy) |
|---|---|---|---|
| Layer | Application (STOMP Protocol) | Transport (Network) | Application (STOMP Protocol) |
| Status | ✅ Active & Maintained | ✅ Stable | ⚠️ Legacy / Deprecated |
| Transport | WebSocket or SockJS | SockJS (XHR/WebSocket) | WebSocket |
| API Style | Modern Class-based | Event Emitter | Callback-based Object |
| Reconnection | Built-in Configuration | Transport Level Only | Manual Implementation |
| TypeScript | ✅ First-class Support | ⚠️ Community Types | ❌ Minimal Support |
For most modern applications, the ideal setup is @stomp/stompjs over native WebSockets. This gives you the structure of STOMP with the performance of standard WebSockets.
// Recommended Architecture
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'wss://api.example.com/stomp',
reconnectDelay: 5000,
onConnect: () => {
client.subscribe('/user/queue/notifications', handleNotification);
}
});
client.activate();
Only introduce sockjs-client if your infrastructure team mandates SockJS for load balancer compatibility or if you must support very old browsers that lack WebSocket support.
// Fallback Architecture
import { Client } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
const client = new Client({
brokerURL: 'https://api.example.com/sockjs',
webSocketFactory: () => new SockJS('https://api.example.com/sockjs'),
});
Avoid stompjs (unscoped) entirely. There is no technical benefit to using the legacy package, and it introduces risk due to lack of maintenance. Migrating to @stomp/stompjs is usually a straightforward refactor that future-proofs your real-time features.
Choose @stomp/stompjs for all new projects requiring STOMP protocol support. It is the actively maintained, TypeScript-ready version that supports STOMP 1.2 and offers flexible transport configuration. It is the direct successor to the legacy stompjs package and provides better error handling, reconnection logic, and modern JavaScript features.
Choose sockjs-client only if your backend server explicitly requires SockJS transport emulation. It is not a STOMP client itself but a transport layer that can be paired with @stomp/stompjs. Use it when you need to support older browsers or network environments that block standard WebSocket connections.
Do NOT choose stompjs (unscoped) for new development. This package is considered legacy and is no longer actively maintained compared to @stomp/stompjs. Migrating to the scoped @stomp/stompjs package ensures access to security patches, modern API patterns, and community support.
STOMP.js is a fully-fledged STOMP over WebSocket library for browsers and Node.js, providing seamless integration with STOMP protocol-compliant messaging brokers.
This library enables clients to connect to STOMP brokers over WebSocket (or TCP). It fully implements the STOMP protocol specifications (v1.0, v1.1, and v1.2), making it compatible with any broker that supports STOMP or STOMP over WebSocket.
Popular brokers like RabbitMQ, ActiveMQ, and others provide support for STOMP and STOMP over WebSockets out-of-the-box.
This section provides a quick guide to integrating STOMP.js into your browser or Node.js application.
To use STOMP.js in a browser:
Add the following in your HTML file:
<script type="importmap">
{
"imports": {
"@stomp/stompjs": "https://ga.jspm.io/npm:@stomp/stompjs@7.0.0/esm6/index.js"
}
}
</script>
<script
async
src="https://ga.jspm.io/npm:es-module-shims@1.5.1/dist/es-module-shims.js"
crossorigin="anonymous"
></script>
Use the library:
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
onConnect: () => {
client.subscribe('/topic/test01', message =>
console.log(`Received: ${message.body}`)
);
client.publish({ destination: '/topic/test01', body: 'First Message' });
},
});
client.activate();
To use STOMP.js in a Node.js environment:
Install the package:
npm install @stomp/stompjs ws
Use it in your application:
import { Client } from '@stomp/stompjs';
import { WebSocket } from 'ws';
Object.assign(global, { WebSocket });
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
onConnect: () => {
client.subscribe('/topic/test01', message =>
console.log(`Received: ${message.body}`)
);
client.publish({ destination: '/topic/test01', body: 'First Message' });
},
});
client.activate();
Comprehensive documentation can be found at: STOMP.js Documentation
If you are updating from an older version of STOMP.js, review the Upgrading Guide for any required changes.
Rx-Stomp builds upon this library, exposing all its features as RxJS Observables, enabling reactive programming patterns.
STOMP.js includes built-in TypeScript definitions, eliminating the need for external type definition files. Begin coding with TypeScript out-of-the-box!
Visit the Change Log for information about changes, improvements, and fixes in recent releases.
Thinking of contributing to STOMP.js? Great! To get started:
We welcome contributions from the community!
This library is made possible by these amazing contributors:
This library is originally based on stompjs by Jeff Mesnil with enhancements and bug fixes from Jeff Lindsay and Vanessa Williams.
Licensed under the Apache-2.0 License. See the LICENSE file for details.