@collab-kit/client
v0.0.17
Published
Browser SDK for adding real-time collaboration to any application. Connects to a Collab-Kit server over WebSockets and provides modules for users, rooms, file storage, broadcasts, and schema-driven KV stores.
Readme
Collab-Kit Client
Browser SDK for adding real-time collaboration to any application. Connects to a Collab-Kit server over WebSockets and provides modules for users, rooms, file storage, broadcasts, and schema-driven KV stores.
Install
npm install @collab-kit/client @collab-kit/utilsQuick Start
import CollabKitClient from '@collab-kit/client';
const client = new CollabKitClient({
serverUrl: 'https://api.collab-kit.com',
authToken: '<jwt-token>', // from POST /users on your server
});
await client.join(); // defaults to { role: 'editor' }
// Viewers can receive updates and follow users, but cannot write collaboration state.
// await client.join({ role: 'viewer' });
console.log(client.currentUser);
console.log(client.currentRoom);Constructor Options
import { defineStores } from '@collab-kit/utils';
const client = new CollabKitClient({
serverUrl: 'https://api.collab-kit.com',
authToken: '<jwt-token>',
stores: defineStores({ /* optional store schemas */ }),
});| Option | Type | Description |
|---|---|---|
| serverUrl | string | Base HTTP(S) URL of the Collab-Kit server |
| authToken | string | JWT returned when creating a user via POST /users |
| stores | StoresConfig | Optional schema config created with defineStores() |
Users
Track all collaborators in the current room.
// Active editor collaborators, excluding the current user
client.users.active; // Map<string, CollabKitUser>
// Explicit paginated cache of all users, excluding the current user.
// This map is only populated by client.users.list(...).
client.users.all; // Map<string, CollabKitUser>
const page = await client.users.list({ pageSize: 100, offset: 0 });
// page: { users, total, pageSize, offset, hasMore }
// Listen for user events
client.users.on('userJoined', (user) => {
console.log(`${user.name} joined`);
});
client.users.on('userLeft', (user) => {
console.log(`${user.name} left`);
});
// Update the current user
await client.currentUser?.update({ name: 'New Name' });pageSize is limited to 100. users.active and users.all both exclude the current user.
Roles
Each SDK connection joins as either an editor or a viewer. Roles are session-scoped and can be changed by the client at any time.
await client.join({ role: 'viewer' });
console.log(client.role); // 'viewer'
await client.updateRole('editor');Editors can write stores, comments, presence, Yjs updates, storage, and broadcasts. Viewers receive realtime updates and can follow/unfollow users, but client-side write methods reject before sending network requests.
User Events
| Event | Payload | Description |
|---|---|---|
| userJoined | CollabKitUser | An editor became active |
| userLeft | CollabKitUser | An editor stopped being active |
| userCreated | CollabKitUser | A new user was added to the room |
| userUpdated | CollabKitUser | A user's fields changed |
| userDeleted | CollabKitUser | A user was removed |
Individual User Events
const user = client.users.all.get(userId);
user.on('updated', (user) => { /* fields changed */ });
user.on('statusChanged', (user) => { /* online <-> offline */ });
user.on('deleted', (user) => { /* removed */ });Room
// Room info is available after joining
console.log(client.currentRoom);
// { id, name, state, active_participants, duration_seconds, ... }
// Fetch room details
const room = await client.room.get();Storage
Upload and manage files scoped to the current room.
// Upload a file
const { key, url } = await client.storage.upload({ file });
// Get a file URL
const url = await client.storage.getUrl({ key });
// List all files
const files = await client.storage.getAll();
// Filter by MIME type
const images = await client.storage.getAll({ mimeType: 'image/' });
// Filter by user
const myFiles = await client.storage.getAll({ userId: client.userId });
// Delete a file
await client.storage.delete({ key });Broadcasts
Send custom events to other participants in the room.
// Send to everyone
client.notifications.broadcast('cursor-move', { x: 100, y: 200 });
// Send to specific users
client.notifications.broadcast('ping', { message: 'hello' }, ['user-id-1']);
// Listen for broadcasts
client.notifications.on('cursor-move', (data) => {
console.log(data); // { x: 100, y: 200 }
});
// Listen once
client.notifications.once('ping', (data) => {
console.log(data.message);
});Stores
Schema-driven KV stores synced in real-time across all connected clients. Define your schemas with defineStores() from @collab-kit/utils and get full type safety.
import CollabKitClient from '@collab-kit/client';
import { defineStores } from '@collab-kit/utils';
const stores = defineStores({
settings: {
theme: { type: 'string', required: true, default: 'light' },
fontSize: { type: 'number', required: true, default: 14 },
notifications: { type: 'boolean', default: true },
},
});
const client = new CollabKitClient({
serverUrl: 'https://api.collab-kit.com',
authToken: '<jwt-token>',
stores,
});
await client.join({ role: 'editor' });
// Fully typed — theme is string, fontSize is number
await client.stores.settings.set({
key: 'user-prefs',
value: { theme: 'dark', fontSize: 16 },
});
const prefs = await client.stores.settings.get({ key: 'user-prefs' });
// prefs: { theme: string; fontSize: number; notifications?: boolean } | null
// Partial updates
await client.stores.settings.update({
key: 'user-prefs',
value: { fontSize: 18 },
});
// List all entries
const all = await client.stores.settings.getAll();
// [{ key: 'user-prefs', value: { theme: 'dark', fontSize: 18, notifications: true } }]
// Delete
await client.stores.settings.delete({ key: 'user-prefs' });Store Events
// Listen for any change in the store
client.stores.settings.on('changed', (event) => {
console.log(event.key, event.action, event.value);
// event.action: 'set' | 'update' | 'delete'
});
// Listen for changes to a specific key
client.stores.settings.on('user-prefs', (value) => {
console.log('prefs changed:', value);
});Comments
Room-scoped comments with replies, reactions, and tags. Comments are synced automatically on join and reconnect.
// Add a top-level comment
const comment = await client.comments.add('Hello world');
// Add a comment with tagged users
const comment = await client.comments.add('Check this out', {
tags: ['user-id-1', 'user-id-2'],
});
// Get all cached top-level comments
const comments = client.comments.getAll();
// Delete a comment (and all its replies)
await client.comments.delete(commentId);
// Sync comments from the server (called automatically on join)
await client.comments.sync();Comment Instance
Each comment returned by add() or getAll() is a CollabKitCommentInstance with methods for replies, reactions, and tags.
// Replies (one level of nesting)
const reply = await comment.reply('Thanks!');
await comment.deleteReply(reply.id);
// Reactions
await comment.addReaction('thumbsup');
await comment.deleteReaction('thumbsup');
// Tags
await comment.addTag(userId);
await comment.deleteTag(userId);
// Access comment data
comment.id; // string
comment.userId; // string
comment.text; // string
comment.reactions; // Record<string, string[]>
comment.tags; // string[]
comment.replies; // CollabKitComment[]
comment.createdAt; // stringComment Events
// Listen for remote changes
client.comments.on('add', (comment) => { /* new comment from another client */ });
client.comments.on('delete', (commentId) => { /* comment deleted */ });
client.comments.on('update', (comment) => { /* reaction/tag/reply change */ });
// Per-comment events
comment.on('update', (comment) => { /* this comment was updated */ });Socket Lifecycle
Access low-level socket events for connection state management.
client.socket.on('connected', () => { /* initial connection */ });
client.socket.on('disconnected', () => { /* socket closed */ });
client.socket.on('reconnecting', () => { /* attempting reconnect */ });
client.socket.on('reconnected', () => { /* reconnected & state rehydrated */ });
client.socket.on('failed', () => { /* reconnection attempts exhausted */ });
client.socket.on('authFailed', () => { /* authentication rejected */ });Disconnect
await client.disconnect();This gracefully notifies other participants before closing the connection.
