Async loading and multiple chat rooms

How to register events before uchat.js loads, how to split events across several chat rooms on one page, and how to attach and clean up chat rooms in an SPA, and how to add a floating widget in the bottom-right corner.

Last updated October 20, 2026

On this page

uchat.js loads with async, so its run order relative to your page scripts is not fixed. This page covers how to register events regardless of load order, how to handle several chat rooms, and how to attach and detach chat rooms in an SPA.

Preload code

Add the code below before uchat.js. It stores events registered before uchat.js is ready in U.events, and they are used as-is once it is ready.

<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>

When uchat.js is ready, U.chat is replaced with the real function. There is one difference between before and after.

Before ready (preload code) After ready
on(name, callback) Stored Registered (after.create for rooms that are already ready is called immediately)
off(name) Removes only entries whose name is exactly the same Removes by label or event name (see below)

Before it is ready, passing only a label such as off('#label') removes nothing. In cleanup code that might run at any time, pass the full name you used in on ('after.message#alarm') to off as-is. This works both before and after it is ready.

Selecting chat rooms

U.chat('*').on('after.join', handler);      // All chat rooms on the page
U.chat('room-a').on('after.join', handler); // room-a only
U.chat('room-b').on('after.join', handler); // room-b only
  • U.chat('room-id') registers for that chat room only, and U.chat('*') registers for all chat rooms on the page (including ones added later).
  • You cannot pass several IDs at once. Register for each one, or register with '*' and branch on room.id inside the callback.
  • If the chat room is already open, U.chat('room-id') returns that chat room's room object. If not, it returns a registrar with only on and off. Either way, you use on and off the same way. → U.chat() and the room object

Labels and removal

Add #label after an event name to remove it by label later.

U.chat('*').on('after.message#alarm', onMessage);
U.chat('*').on('after.user.join#alarm', onJoin);

U.chat('*').off('#alarm');              // Everything labeled alarm
U.chat('*').off('after.message');       // All after.message (any label)
U.chat('*').off('after.message#alarm'); // Only entries matching both

U.chat('*').off(...) removes not only what was registered with '*' but also everything registered per chat room. To avoid removing events registered by other scripts (plugins, other widgets), give your events a unique label (such as #my-widget) and remove them by label.

after.create and readiness

after.create fires when the chat room screen (skin) is ready. It comes before joining, so set up the Skin UI API and channels inside it.

If you register after.create on a chat room that is already ready, it is called once right away without waiting. So you do not miss it even if your script runs late.

U.chat('*').on('after.create#my-widget', function (room) {
	if (room.id !== 'my-room') return;
	// Use room.skin, room.plugin
});

Adding and removing tags, changing attributes

uchat.js watches the <u-chat> tags on the page.

  • Add a <u-chat> to the page and the chat room opens. Remove it and the chat room closes.
  • When a chat room closes, events registered with that chat room ID are removed with it. Events registered with '*' remain. If you need the events again when you re-add the tag, register them again or use '*'.
  • Only one tag per room ID opens on a page. A second tag is ignored, and removing the first tag does not make the second one open in its place.
  • Changes to attributes such as room and user_data are not detected. To change a value, remove the tag and add it again with the new attributes.
function replaceRoom(container, roomId) {
	container.replaceChildren(); // Remove the existing <u-chat> → chat room closes
	var tag = document.createElement('u-chat');
	tag.setAttribute('room', roomId);
	tag.style.cssText = 'display:block; width:100%; height:500px;';
	container.appendChild(tag); // New chat room opens
}

Using in an SPA

In an SPA, load uchat.js only once when a chat room enters the screen, and clean up your events when you leave the screen. When the framework removes the <u-chat> tag, the chat room closes with it.

The uchat() function below does the same job as the preload code, so it is fine if it runs before uchat.js.

// uchat.js — a framework-agnostic helper
export function uchat(roomId) {
	const U = (window.U = window.U || {});
	U.events = U.events || [];
	if (!U.chat) {
		U.chat = (n) => ({
			on: (e, t) => U.events.push([n, e, t]),
			off: (e) => {
				for (let t = U.events.length; t > 0; )
					U.events[--t][0] == n && U.events[t][1] == e && U.events.splice(t, 1);
			}
		});
	}
	if (!document.querySelector('script[data-uchat]')) {
		const s = document.createElement('script');
		s.src = 'https://client.uchat.io/uchat.js';
		s.async = true;
		s.dataset.uchat = '';
		document.head.appendChild(s);
	}
	return U.chat(roomId);
}

React:

import { useEffect } from 'react';
import { uchat } from './uchat';

export default function ChatRoom({ roomId }) {
	useEffect(() => {
		const name = 'after.message#my-widget-' + roomId;
		uchat('*').on(name, (room, data) => {
			if (room.id !== roomId) return;
			console.log(data.user.nick, data.content);
		});
		return () => uchat('*').off(name);
	}, [roomId]);

	// Changing key creates a new tag, so it switches to the other chat room
	return <u-chat key={roomId} room={roomId} style={{ display: 'block', width: '100%', height: '500px' }} />;
}
  • Register events with '*' and branch on room.id. Events registered with a chat room ID are removed along with the tag, so registering with '*' keeps a remount from losing events to that removal order.
  • When cleaning up, pass the full name you used in on to off. It is removed even if uchat.js is not ready yet.
  • Changing only the room attribute does not switch chat rooms, so use key to create a new tag.

In Vue, call on in onMounted and off in onUnmounted, and register u-chat as a custom element (see 5-minute setup). In Svelte, register on in onMount and call off in the cleanup function it returns. To switch chat rooms, create a new tag with :key in Vue and a {#key roomId} block in Svelte.

Floating widget

To attach a chat room as a button in the bottom-right corner of the page, use the widget from the @u-chat/embed npm package. While closed, it shows new messages as a preview with an unread count, and clicking it opens the chat. You can also generate this code in the Floating widget tab of the install code screen on the admin page, and pick the attributes below, plus position and size, under Widget settings on that screen.

Sites without a bundler (HTML, Gnuboard, WordPress, and so on) load it with one <script> line. You can add it to a page that already loads uchat.js.

<script src="https://cdn.jsdelivr.net/npm/@u-chat/embed@latest/dist/widget.global.js"></script>
<u-chat-widget launcher-text="{user_count} online">
	<u-chat room="my-room"></u-chat>
</u-chat-widget>

To load it from the chat server instead of jsDelivr, use https://client.uchat.io/widget.js. An independent server uses its own widget.js, and the widget connects to the uchat.js on that same server, so no src attribute is needed.

React (npm install @u-chat/react):

import { UchatWidget } from '@u-chat/react';

export default function App() {
	return <UchatWidget room="my-room" launcherText="{user_count} online" />;
}

Svelte 5 (npm install @u-chat/svelte):

<script>
	import { UchatWidget } from '@u-chat/svelte';
</script>

<!-- Pass the text as {'...'} so Svelte does not read the braces as an expression -->
<UchatWidget room="my-room" launcherText={'{user_count} online'} />

Vue 3 (npm install @u-chat/vue). No isCustomElement setting is needed.

<script setup>
import { UchatWidget } from '@u-chat/vue';
</script>

<template>
	<UchatWidget room="my-room" launcher-text="{user_count} online" />
</template>

Widget attributes (camelCase such as launcherText in React and Svelte):

Attribute Description
launcher-text Button text. {user_count} becomes the user count and {unread_count} the unread count
variant bubble (default, round button), bar (long bar at the bottom that opens as a bottom sheet) or side (right-edge sidebar: open from the start at full screen height, pushing the page content to the left; folding it leaves a vertical tab on the right edge. Screens narrower than 768px start folded and overlay when opened)
launcher-color, launcher-image Button background color and background image URL (the image wins)
panel-title Title above the open chat
preview-duration How long a new message preview stays (ms, default 5000)
lang Language of the widget buttons and messages (ko, en, ja). Without it: the inner <u-chat lang> → the page <html lang> → the browser language; other languages get English
src uchat.js URL. For an independent server, use that server's URL
  • For member-integrated chat rooms, put user_data on the inner <u-chat> (userData in React, Svelte and Vue). On the install code screen, Placement → Floating widget gives widget code with the signature already in it.
  • The widget joins the chat room as soon as the page opens, so the preview and user count show even while it is closed. Past chat (past in after.message) and your own messages are not counted as unread.
  • Hiding it with × disconnects and remembers the hidden state for this chat room only. A small button to turn it back on stays in the bottom-right corner.
  • From scripts, call open(), close(), toggle(), maximize(), restore(), hide(), show(), and destroy(). In React, Vue and Svelte, put a ref (bind:this in Svelte) on the component and call the same names.
  • If uchat.js cannot be loaded, the widget gets an uchat-widget-error event (event.detail is the error).

To create the widget from a script instead of a tag, use createUchatWidget(target, options). It is on window.UchatEmbed with the one-line script and in @u-chat/embed/widget with npm.

const widget = UchatEmbed.createUchatWidget(document.body, {
	chat: { room: 'my-room', user_data: signedUserData },
	widget: { launcherText: '{user_count} online', variant: 'bar' },
});
widget.addEventListener('uchat-widget-error', (event) => console.error(event.detail));
widget.open();

Change the position and size with CSS variables.

u-chat-widget {
	--uchat-widget-right: 20px;
	--uchat-widget-bottom: 20px;
	--uchat-widget-panel-width: 380px;
	--uchat-widget-panel-height: 640px;
	--uchat-widget-side-width: 360px;
	--uchat-widget-accent: #ff7e25;
	--uchat-widget-z-index: 2147483000;
}

Still not sure how it works? Ask your question on the contact board.

Business name UCHAT · CEO 김경욱 · Business registration no. 141-05-32832
Address 경기도 파주시 와석순환로 61 · Mail-order business registration no. 2013-경기파주-4485호
Phone 010-8677-8633 · Email support@uchat.io

ⓒ 2013–2026 UCHAT. All rights reserved.