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 provides a lightweight plugin system that lets you attach reusable objects to every Connection instance. A plugin can register stanza handlers, listen to connection lifecycle events, and expose helper methods — all under a namespace of your choosing. Once registered, a plugin is automatically instantiated for every new connection, including those created before the plugin was loaded.

How Plugins Work

When you call Strophe.addConnectionPlugin(name, ptype), Strophe.js stores the prototype object in an internal connectionPlugins registry. The Connection constructor iterates over that registry and, for each plugin, creates a new object that inherits from the registered prototype and calls its init(connection) method. The resulting instance is attached to the connection as connection[name].
connectionPlugins['roster'] = RosterPlugin;

// Inside new Connection():
connection['roster'] = Object.create(RosterPlugin);
connection['roster'].init(connection);
Connection status changes are broadcast to every plugin that has a statusChanged(status, condition) method. This happens in _changeConnectStatus() before the user’s own callback is invoked.

Plugin Shape

A plugin prototype needs at most two methods:
MethodSignatureWhen it is called
init(conn: Connection) => voidOnce, immediately after the Connection is created
statusChanged(status: number, condition?: string | null) => voidEvery time the connection status changes
Both methods are optional — you only need to implement the ones that are relevant to your plugin.

Registering a Plugin

import { Strophe } from 'strophejs';

const RosterPlugin = {
  init(conn: typeof Strophe.Connection.prototype) {
    this.connection = conn;
    // Register stanza handlers once the plugin is initialised
    this.connection.addHandler(
      (iq) => this._onRosterPush(iq),
      'jabber:iq:roster',
      'iq',
      'set'
    );
  },

  statusChanged(status: number, _condition?: string | null) {
    if (status === Strophe.Status.CONNECTED) {
      this.fetchRoster();
    }
  },

  fetchRoster() {
    const iq = Strophe.$iq({ type: 'get' }).c('query', {
      xmlns: 'jabber:iq:roster',
    });
    this.connection.sendIQ(iq, (result) => {
      console.log('Roster received:', result);
    });
  },

  _onRosterPush(iq: Element) {
    console.log('Roster push received:', iq);
    return true; // keep the handler active
  },
};

// Register globally — affects all future Connection instances
Strophe.addConnectionPlugin('roster', RosterPlugin);
addConnectionPlugin must be called before creating any Connection that should have the plugin. Plugins registered after a connection is constructed are not retroactively initialised on that connection.

Using a Plugin After Connecting

After addConnectionPlugin has been called, every new Connection instance exposes the plugin under the registered name:
const connection = new Strophe.Connection('/xmpp-websocket/');

connection.connect('user@example.com', 'password', (status) => {
  if (status === Strophe.Status.CONNECTED) {
    // The plugin is available as connection.roster
    connection.roster.fetchRoster();
  }
});
TypeScript users can augment the Connection type to get full type safety:
declare module 'strophejs' {
  interface Connection {
    roster: typeof RosterPlugin;
  }
}

A Complete Plugin Example

The following plugin sends a directed presence to a conference room every time the connection status becomes CONNECTED, and cleans up when it becomes DISCONNECTED.
import { Strophe, $pres } from 'strophejs';

const MucAutoJoinPlugin = {
  rooms: [] as string[],

  init(conn: typeof Strophe.Connection.prototype) {
    this.connection = conn;
  },

  statusChanged(status: number) {
    if (status === Strophe.Status.CONNECTED) {
      this.rooms.forEach((room) => this._joinRoom(room));
    } else if (status === Strophe.Status.DISCONNECTED) {
      this.rooms = [];
    }
  },

  addRoom(roomJid: string) {
    this.rooms.push(roomJid);
  },

  _joinRoom(roomJid: string) {
    const pres = $pres({ to: `${roomJid}/${this.connection.jid.split('@')[0]}` })
      .c('x', { xmlns: 'http://jabber.org/protocol/muc' });
    this.connection.send(pres);
  },
};

Strophe.addConnectionPlugin('mucAutoJoin', MucAutoJoinPlugin);

// Usage
const connection = new Strophe.Connection('/xmpp-websocket/');
connection.mucAutoJoin.addRoom('general@conference.example.com');

connection.connect('user@example.com', 'password', (status) => {
  if (status === Strophe.Status.CONNECTED) {
    console.log('Joined all auto-join rooms');
  }
});

Plugin Initialisation Order

Plugins are initialised in the order in which their keys appear in the connectionPlugins object, which follows JavaScript’s property insertion order. If plugin B depends on plugin A being fully initialised, register plugin A first:
Strophe.addConnectionPlugin('base', BasePlugin);   // initialised first
Strophe.addConnectionPlugin('roster', RosterPlugin); // can safely depend on 'base'
Within init(), you can access other plugins on the same connection through the conn argument:
init(conn) {
  this.connection = conn;
  // Access another plugin on the same connection
  this.base = conn.base;
}

Community Plugins

The Strophe.js organisation maintains a collection of ready-made XMPP extension plugins at: https://github.com/strophe/strophejs-plugins Available plugins include implementations of common XEPs such as MUC (XEP-0045), PubSub (XEP-0060), vCards (XEP-0054), and many more. Each plugin follows the same init / statusChanged pattern described on this page.

Build docs developers (and LLMs) love