Quickstart
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 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".
| URL | Server | Room |
|---|---|---|
https://community.divcord.gg/embed/general |
Official Server | general |
https://my-server.divcord.gg/embed/lounge |
my-server |
lounge |
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.
| Argument | Type | Notes |
|---|---|---|
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
| Member | Type | What 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);
| Method | Notes |
|---|---|
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.
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);
});
| Method | Notes |
|---|---|
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 for | Event | Aliases |
|---|---|---|
| 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
| Type | Payload |
|---|---|
ready | { path, href, themes, events } |
pong | { t } after a ping |
info | Result of getInfo |
event | { event, data } wrapper for chat events |
message | Raw 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
| Event | Typical payload |
|---|---|
connect | none (replayed if already connected when the hook attaches) |
disconnect | socket reason string |
error | { message } (Error objects are serialized) |
authenticated | public user object |
joined_room | join result (room, messages, notifications) |
left_room | room id string |
new_message | message object plus roomId |
message_edited | edited message fields |
message_deleted | { roomId, id } |
message_reactions | reaction update |
message_pinned | { roomId, id, pinned, pinnedAt, pinnedBy } |
user_joined | { roomId, user, users } |
user_left | { roomId, user, users } |
user_updated | updated public user plus optional users |
users | online 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_unmuted | moderation update for a target in the room |
moderator_added | { username, ... } |
kicked / banned | action against the signed-in user |
muted / unmuted | action against the signed-in user |
account_deleted | none |
room_deleted | room id / metadata |
room_lock_updated | { roomId, locked } |
emojis_updated | custom emoji map |
poked | poke payload |
profile_updated | updated self user |
payload | room 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) {});
| Method | Returns |
|---|---|
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
- Ack methods time out after 8 seconds and reject with
timeout waiting for <type>. - Types ending in
:errorreject withpayload.message. - Snapshot getters resolve
{ ok: false, error: 'Chat UI not ready' }if the room has not finished loading. getProfilefails the same way if the room is not ready. A failed lookup comes back as{ ok: false, error }.setCSSFromURLrejects if the CSS fetch is not OK (status text included).destroy()rejects every in-flight call withdestroyed.- Payloads are JSON-cloned before they leave the iframe. Errors become
{ message }.