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.

Strophe.js keeps the core workflow simple: create a Connection, call connect() with your JID, password, and a status callback, then register stanza handlers and start sending. The five steps below walk from installation to a fully working echobot — a program that replies to every chat message it receives — to show all the moving parts in context.

Step-by-step: Building an Echobot

1

Install Strophe.js

Add the package to your project. In Node.js also install the peer dependencies that provide DOM and WebSocket APIs:
npm install strophe.js
# Node.js only:
npm install jsdom ws
2

Create a Connection

Import the library and instantiate a Connection. The transport is selected automatically from the service URL.
import { Strophe, $pres, $msg } from 'strophe.js';

const connection = new Strophe.Connection(
  'wss://chat.example.com/websocket'
);
Strophe.Connection accepts an optional second argument — a ConnectionOptions object — where you can configure things like custom SASL mechanisms, cookies, keepalive, and more. Omitting it uses the defaults.
3

Connect with a Status Callback

Call connection.connect() with the user’s JID, their password, and a callback function. Strophe.js calls the callback multiple times as the connection moves through its lifecycle, passing a numeric status code from Strophe.Status.
function onConnect(status: number): void {
  if (status === Strophe.Status.CONNECTING) {
    console.log('Connecting…');
  } else if (status === Strophe.Status.CONNFAIL) {
    console.log('Connection failed.');
  } else if (status === Strophe.Status.DISCONNECTING) {
    console.log('Disconnecting…');
  } else if (status === Strophe.Status.DISCONNECTED) {
    console.log('Disconnected.');
  } else if (status === Strophe.Status.CONNECTED) {
    console.log('Connected! JID:', connection.jid);
    // Register handlers and send initial presence here.
    onConnected();
  }
}

connection.connect('user@example.com', 's3cr3t', onConnect);
The full set of Strophe.Status constants:
ConstantValueMeaning
ERROR0An unrecoverable error occurred
CONNECTING1TCP/WebSocket handshake in progress
CONNFAIL2Connection attempt failed
AUTHENTICATING3SASL exchange in progress
AUTHFAIL4Authentication rejected by server
CONNECTED5Session is open and authenticated
DISCONNECTED6Connection is fully closed
DISCONNECTING7Graceful shutdown in progress
ATTACHED8Attached to an existing BOSH session
REDIRECT9Server issued a redirect
CONNTIMEOUT10Disconnect timeout elapsed
BINDREQUIRED11Server requires explicit resource binding
ATTACHFAIL12Session attach attempt failed
RECONNECTING13Automatic reconnect in progress
4

Add Stanza Handlers

Inside your CONNECTED branch, register handlers with connection.addHandler(). A handler receives the matching stanza as a DOM Element and must return true to stay registered — returning false (or nothing) removes the handler after the first match.
function onConnected(): void {
  // Listen for all incoming <message> stanzas.
  connection.addHandler(
    onMessage,   // callback
    null,        // namespace (null = any)
    'message',   // stanza name
    null,        // type attribute (null = any)
    null,        // id attribute (null = any)
    null         // from JID (null = any)
  );

  // Announce presence so the server delivers stanzas to us.
  connection.send($pres().tree());
}
5

Send Stanzas and Disconnect

Strophe.js provides four stanza builder helpers that mirror XMPP’s core stanza types. Call .tree() on the builder to get the underlying DOM element before passing it to send().
function onMessage(stanza: Element): boolean {
  const from = stanza.getAttribute('from');
  const type = stanza.getAttribute('type');
  const to   = stanza.getAttribute('to');
  const bodyElements = stanza.getElementsByTagName('body');

  if (type === 'chat' && bodyElements.length > 0) {
    const bodyText = Strophe.getText(bodyElements[0]);
    console.log(`Message from ${from}: ${bodyText}`);

    // Echo the body back to the sender.
    const reply = $msg({ to: from, from: to, type: 'chat' })
      .cnode(Strophe.copyElement(bodyElements[0]));

    connection.send(reply.tree());
    console.log(`Echoed to ${from}: ${bodyText}`);
  }

  // Return true to keep this handler alive for the next message.
  return true;
}
To shut down cleanly, call disconnect(). Strophe.js sends an unavailable presence stanza, waits up to 3 seconds (configurable via disconnection_timeout), then closes the transport.
// Graceful shutdown — sends <presence type="unavailable"/> first.
connection.disconnect();

Complete Echobot Example

The snippet below combines all five steps into a self-contained module. It imports from strophe.js, connects over WebSocket, and echoes every incoming chat message.
import { Strophe, $pres, $msg } from 'strophe.js';

const XMPP_SERVICE = 'wss://chat.example.com/websocket';
const JID          = 'echobot@example.com';
const PASSWORD     = 's3cr3t';

const connection = new Strophe.Connection(XMPP_SERVICE);

function onMessage(stanza: Element): boolean {
  const from = stanza.getAttribute('from');
  const to   = stanza.getAttribute('to');
  const type = stanza.getAttribute('type');
  const bodyElements = stanza.getElementsByTagName('body');

  if (type === 'chat' && bodyElements.length > 0) {
    const bodyText = Strophe.getText(bodyElements[0]);
    console.log(`ECHOBOT: Message from ${from}: ${bodyText}`);

    const reply = $msg({ to: from, from: to, type: 'chat' })
      .cnode(Strophe.copyElement(bodyElements[0]));

    connection.send(reply.tree());
    console.log(`ECHOBOT: Replied to ${from}`);
  }

  // Must return true to keep the handler registered.
  return true;
}

function onConnect(status: number): void {
  if (status === Strophe.Status.CONNECTING) {
    console.log('Connecting…');
  } else if (status === Strophe.Status.CONNFAIL) {
    console.log('Connection failed.');
  } else if (status === Strophe.Status.DISCONNECTING) {
    console.log('Disconnecting…');
  } else if (status === Strophe.Status.DISCONNECTED) {
    console.log('Disconnected.');
  } else if (status === Strophe.Status.CONNECTED) {
    console.log('Connected as', connection.jid);
    console.log('Send a chat message to', connection.jid, 'to test the echobot.');

    connection.addHandler(onMessage, null, 'message', null, null, null);
    connection.send($pres().tree());
  }
}

connection.connect(JID, PASSWORD, onConnect);

Debugging with rawInput / rawOutput

Override connection.rawInput and connection.rawOutput to log the raw wire traffic. This is the fastest way to diagnose authentication failures, unexpected stanza shapes, or transport-level issues.
connection.rawInput  = (data: string): void => { console.log('RECV:', data); };
connection.rawOutput = (data: string): void => { console.log('SENT:', data); };
Assign these before calling connect() so you capture the full stream from the very first byte. If you only need parsed XML rather than raw strings, override connection.xmlInput and connection.xmlOutput instead — they receive DOM Element objects.

Build docs developers (and LLMs) love