embed

Embed a room on your site

Use embed.js on your page to theme an iframe chat room, listen for chat events, and fetch current user, room, and server settings. The script is only used to communicate with the iframe for custom scripting and more control.

Quickstart

New here? The interactive demo walks you through creating a server, embedding a room from your server into your website, and customizing it with CSS. This page is the API reference when you are ready to learn more about what you can control.
Minimal embed. You can iframe a room with no script at all. Use embed.js only when your page needs themes, events, or state.

Load the script on your page, set the src of an iframe to an embed URL, then call Divcord.connect. Wait for bridge.ready before sending commands.

Script located at https://divcord.gg/embed.js, hotlinking with a script tag is preferred so you are always running the latest code, or you can download the latest version directly from the URL and host it locally.

<script src="https://divcord.gg/embed.js"></script>
<iframe id="chat" src="https://my-server.divcord.gg/embed/general"></iframe>
<script>
  const bridge = Divcord.connect(document.getElementById('chat'));

  bridge.ready.then(function () {
    return bridge.setTheme('aurora');
  }).then(function () {
    bridge.on('new_message', function (msg) {
      console.log(msg);
    });
    return bridge.getState();
  }).then(function (state) {
    console.log(state.user, state.room, state.online);
  });
</script>
Bridge? In the code snippets you find here, we set our Divcord instance to a variable named bridge since it acts as the communication bridge between your site and our iframe. If you want to embed multiple chats, you will need to come up with unique variable names for each divcord instance.

Embed URLs

Embed URLs can be obtained for each room you own on the dashboard. Open the dashboard, navigate to the rooms tab, select the actions button on the room you want to embed, and click "Embed".

URLServerRoom
https://community.divcord.gg/embed/general Official Server general
https://my-server.divcord.gg/embed/lounge my-server lounge
Embed URL Syntax if you are quickly writing or batch writing embed URLs you can convert a regular room URL e.g. https://my-server.divcord.gg/#general to an embed URL by removing the # and adding the /embed folder before the room name, e.g. https://my-server.divcord.gg/embed/general

Divcord.connect(iframe, options)

Creates a bridge to one iframe.

ArgumentTypeNotes
iframe HTMLIFrameElement Required.
options.origin string Expected iframe origin. Default is derived from iframe.src. Falls back to * if the src cannot be parsed.
const bridge = Divcord.connect(iframe, {
  origin: 'https://my-server.divcord.gg'
});

Lifecycle

MemberTypeWhat it does
bridge.ready Promise<void> Resolves when the iframe posts ready (or after a late attach).
bridge.isReady boolean True after the first ready event.
bridge.destroy() void Removes the message listener, rejects pending calls, clears event listeners.

Theme and CSS

You can apply custom CSS rules inside the iframe and override the room's default stylesheet, or pick from an existing Divcord theme.

bridge.setTheme(name, opts)

Loads a Divcord theme into the iframe. Default opts.reset is true, which completely disables the embed's default stylesheets first (except widgets, icons, fonts, code syntax highlighting, the user-list menu sheet, and other niche features).

Divcord themes match the names from the settings menu, default, aurora, business, frutiger-aero, irc, lightcast, etc.

bridge.setTheme('aurora');

bridge.setCSS(css, opts)

Inject raw CSS. Pass { reset: true } to disable most of the embed's default stylesheets.

bridge.setCSS(`
  body { background: #0f172a !important; }
  #embed-bar { background: #1e293b !important; }
`, { reset: true });

bridge.setCSSFromURL(url, opts)

Fetches CSS on the source url. The stylesheet must be CORS-readable from the source domain. Same reset option as setCSS.

bridge.clearCSS()

Removes any styles added by you and re-enables stylesheets that were disabled by reset if any.

Flair

Writes a CSS class flair onto the signed-in embed account. The flair text is applied as a class on the username in the online list, messages, and profile cards. The flair text is trimmed to 24 characters of [A-Za-z0-9_-]. Spaces become hyphens. Some flair names are reserved such as mod, moderator, owner, admin.

bridge.setFlair('subscriber');
bridge.clearFlair();
bridge.setFlair(''); // also clears current flair

Safe to call right after bridge.ready. If the embed user is not signed in yet or the socket is still connecting, the flair is queued and applied after login.

JSON payloads

Send a JSON value from your page to every client in that room. Payloads are not stored anywhere on the database and do not execute any code natively.

bridge.sendPayload({ type: 'score', value: 12, player: 'alice' });
bridge.sendPayload({ ready: true });

bridge.on('payload', function (packet) {
  if (packet.data && packet.data.type === 'score') {
    console.log(packet.data.value, packet.from.username);
  }
});

bridge.off('payload', handler);
MethodNotes
sendPayload(data) Sends a JSON-serializable value (packets >8 KB are dropped/ignored by the servers). Add your own key if you want to sort packets, e.g. { type: 'score', value: 12 }.
on('payload', fn) Every inbound payload. Returns an unsubscribe function.
off('payload', fn) Removes that listener.

Inbound packet shape: { roomId, data, from, timestamp }. from is the public user object of the sender. The sender also receives their own payload.

The embed user must be signed in and in the room to send or receive payloads. Muted, banned, and locked-room rules apply to packet sends. Payloads are rate limited (10 per 2 seconds per socket).

Events

bridge.on and bridge.off are the listener API. Use them for payloads, chat lines, edits, deletes, and the online list.

function onChat(msg) {
  console.log(msg.username, msg.content);
}

bridge.on('new_message', onChat);
bridge.on('message_edited', function (msg) {});
bridge.on('message_deleted', function (data) {});
bridge.on('users', function (data) {
  console.log(data.users, data.roomId);
});
bridge.on('payload', function (packet) {
  console.log(packet.data);
});

bridge.off('new_message', onChat);
bridge.once('ready', function () {});
bridge.on(['new_message', 'message_edited'], onChat);
bridge.on('*', function (name, payload) {
  console.log('event', name, payload);
});
MethodNotes
on(type, fn) Returns an unsubscribe function. type may be a string or an array of names. on('*', fn) receives (name, payload).
off(type, fn) Removes one listener. Same string or array form as on.
once(type, fn) Fires once, then unsubscribes.
bridge.events Array of known chat event names forwarded from the iframe.

Common listener names

Listen forEventAliases
New chat line new_message messages, chat
Edited message message_edited edited, updated, message_updated
Deleted message message_deleted deleted
Online list change users user_list, online
JSON payload payload payloads

users fires on user_joined, user_left, user_updated, and room_presence. The payload still has users and usually roomId. Do not use on('message') for chat lines.

Bridge protocol events

TypePayload
ready{ path, href, themes, events }
pong{ t } after a ping
infoResult of getInfo
event{ event, data } wrapper for chat events
messageRaw envelope for every inbound message
setCSS:ok / setTheme:ok / …Ack payloads

Chat events arrive as type event and are also re-emitted under the inner name. bridge.on('new_message', fn) is the usual listener.

Forwarded chat events

EventTypical payload
connectnone (replayed if already connected when the hook attaches)
disconnectsocket reason string
error{ message } (Error objects are serialized)
authenticatedpublic user object
joined_roomjoin result (room, messages, notifications)
left_roomroom id string
new_messagemessage object plus roomId
message_editededited message fields
message_deleted{ roomId, id }
message_reactionsreaction update
message_pinned{ roomId, id, pinned, pinnedAt, pinnedBy }
user_joined{ roomId, user, users }
user_left{ roomId, user, users }
user_updatedupdated public user plus optional users
usersonline list after a join, leave, profile change, or room_presence
room_presence{ roomId, users, userCount } (also when you are not in that room)
user_muted / user_unmutedmoderation update for a target in the room
moderator_added{ username, ... }
kicked / bannedaction against the signed-in user
muted / unmutedaction against the signed-in user
account_deletednone
room_deletedroom id / metadata
room_lock_updated{ roomId, locked }
emojis_updatedcustom emoji map
pokedpoke payload
profile_updatedupdated self user
payloadroom JSON packet ({ roomId, data, from, timestamp })

If you connect after the room is already signed in, you still get connect, authenticated, and joined_room so you do not miss the current state.

Snapshots and lookups

These ask the iframe for current state. They return a Promise. Application failures use { ok: false, error } instead of throwing, except where noted.

bridge.getOnlineList().then(function (online) {
  // { ok, roomId, count, users }
});

bridge.getUser().then(function (me) {
  // { ok, authenticated, user }
});

bridge.getRoom().then(function (room) {
  // { ok, room: { id, name, createdBy, private, locked, moderators, notifications, userCount, users } }
});

bridge.getServer({ rooms: true }).then(function (server) {
  // { ok, server: { name, origin, href, path, connected }, rooms }
});

bridge.getState({ rooms: true }).then(function (state) {
  // { ok, user, authenticated, online, onlineCount, room, server, rooms }
});

bridge.getProfile('alice').then(function (profile) {});
bridge.getInfo().then(function (info) {});
MethodReturns
getOnlineList() Users in the active embed room (same list as the sidebar).
getUser() Signed-in public user, or user: null if not authenticated yet.
getRoom() Active room metadata plus the current online list. room is null if not joined.
getServer({ rooms }) Server name, origin, path, socket connected flag. rooms: true also calls listRooms.
getState({ rooms }) Combined user, online list, room, and server snapshot.
getProfile(username) Live profile fetch over the embed socket. Omit username to use the signed-in account.
getInfo() path, ready, themes, events, room, compact user and server.

Public user objects typically include username, isAnonymous, avatarUrl, flair, createdAt, timezone (omitted when the user opts to hide it), showTimezone, bio, and link.

Errors and timing