Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/strophe/strophejs/llms.txt

Use this file to discover all available pages before exploring further.

When a user opens your application in several browser tabs at the same time, each tab normally creates its own WebSocket connection to the XMPP server. The Strophe.js shared-worker transport avoids this by running a single WebSocket inside a SharedWorker and multiplexing all tabs through it. Every Connection instance points its WorkerWebsocket transport at the shared worker script, which acts as a ConnectionManager that fans out incoming messages to all connected ports.

How It Works

The architecture has three layers:
  1. Connection (main thread) — the regular Strophe.js connection object your application code interacts with.
  2. WorkerWebsocket (main thread) — a subclass of Websocket that, instead of opening a raw WebSocket, opens a SharedWorker and communicates with it over MessagePort.
  3. shared-connection-worker.ts (worker thread) — the ConnectionManager class that owns the single real WebSocket, forwards all messages to every registered port, and accepts send/close commands from any port.
Tab A ──► WorkerWebsocket ──┐
                             ├──► ConnectionManager (SharedWorker)
Tab B ──► WorkerWebsocket ──┘         └──► WebSocket ──► XMPP Server

Setup

1

Build or copy the worker script

The shared worker script is located at src/shared-connection-worker.ts in the Strophe.js source tree. Build your project so that it is available as a standalone browser-compatible JavaScript file at a URL your application can reference.
# Example: copy the compiled worker to your public directory
cp dist/shared-connection-worker.js public/shared-connection-worker.js
2

Pass the `worker` option to the Connection constructor

Set the worker option to the URL of the shared worker script. Strophe.js detects the presence of this option and automatically selects WorkerWebsocket as the transport, regardless of the service URL scheme.
import { Strophe } from 'strophejs';

const connection = new Strophe.Connection('/xmpp-websocket/', {
  worker: '/shared-connection-worker.js',
});
3

Connect normally

From this point the API is identical to a regular WebSocket connection. Call connection.connect() from each tab; the worker handles opening the underlying socket only once.
connection.connect('user@example.com', 'password', (status, condition) => {
  if (status === Strophe.Status.CONNECTED) {
    console.log('Connected via shared worker');
  } else if (status === Strophe.Status.ATTACHED) {
    console.log('Attached to existing shared-worker session');
  } else if (status === Strophe.Status.ATTACHFAIL) {
    console.error('Failed to attach to shared-worker session');
  }
});
4

Handle tab attach / reattach

When a second tab opens after the first has already authenticated, the WorkerWebsocket sends an _attach command to the worker. The ConnectionManager checks whether the socket is still open and responds with either ATTACHED or ATTACHFAIL. The second tab’s WorkerWebsocket._attachCallback() translates this into the appropriate Strophe.Status for your callback.

Full Example

import { Strophe } from 'strophejs';

const connection = new Strophe.Connection('/xmpp-websocket/', {
  worker: '/shared-connection-worker.js',
});

// Optional: log raw XML traffic
connection.rawInput = (data) => console.debug('RECV', data);
connection.rawOutput = (data) => console.debug('SENT', data);

function onConnect(status: number, condition?: string | null) {
  switch (status) {
    case Strophe.Status.CONNECTING:
      console.log('Connecting…');
      break;
    case Strophe.Status.CONNECTED:
      console.log('Connected as', connection.jid);
      // Register stanza handlers here
      break;
    case Strophe.Status.ATTACHED:
      console.log('Attached to existing session for', connection.jid);
      break;
    case Strophe.Status.ATTACHFAIL:
      console.error('Failed to attach to shared session — re-authenticating');
      connection.connect('user@example.com', 'password', onConnect);
      break;
    case Strophe.Status.CONNFAIL:
      console.error('Connection failed:', condition);
      break;
    case Strophe.Status.DISCONNECTED:
      console.log('Disconnected.');
      break;
  }
}

connection.connect('user@example.com', 'password', onConnect);

WorkerWebsocket vs. Websocket

WorkerWebsocket extends Websocket and overrides the parts that deal with the raw socket:
AspectWebsocketWorkerWebsocket
Socket ownerMain threadConnectionManager inside SharedWorker
_connect()Opens new WebSocket(...)Posts ['_connect', service, jid] to the worker port
_disconnect()Sends <close/> then closes socketPosts <close/> string to the worker port
_onOpen / _onClose / _onMessageBound directly to the socketForwarded via MessagePort events from the worker
_replaceMessageHandler()Swaps socket.onmessageSwaps an internal _messageHandler reference
The ConnectionManager in the worker script keeps a ports array. Every time a new tab connects, its port is added to the array. Incoming WebSocket messages are broadcast to all ports; outgoing sends go directly from each port to the socket.

Browser Compatibility

SharedWorker is not universally supported. Safari added support in version 16.4 (released March 2023). iOS Safari and some WebViews may still lack support. Check MDN compatibility data before relying on this feature in production.
To handle environments where SharedWorker is unavailable, check for its existence before using the worker option and fall back to a regular WebSocket connection:
import { Strophe } from 'strophejs';

const options = typeof SharedWorker !== 'undefined'
  ? { worker: '/shared-connection-worker.js' }
  : {};

const connection = new Strophe.Connection('/xmpp-websocket/', options);
The shared-worker transport only works with WebSocket endpoints. Passing a BOSH (http:// or https://) URL together with the worker option is not supported. The worker option takes precedence over the URL scheme — Strophe.js uses WorkerWebsocket whenever worker is set.

Build docs developers (and LLMs) love