@nolag/chat
Multi-room chat with presence, typing indicators, message replay, and user mapping.
Overview
@nolag/chat is a high-level SDK that turns any application into a fully-featured chat system. It handles room management, user presence, typing indicators, message history replay, and unread tracking, all on top of @nolag/js-sdk. Your app owns one core NoLag client and injects it into NoLagChat; the wrapper attaches its chat behaviour to that connection. You create a NoLagChat instance, wait for it to be ready, join rooms, and start sending messages within minutes.
Key Features
- Multi-room chat with isolated presence per room
- Typing indicators with automatic timeout
- Message replay to catch up on up to 7 days of history after reconnect
- User mapping to attach names, avatars, and metadata to each actor
- Unread message tracking with per-room badge counts
- Automatic reconnection with state restoration
How It Works
NoLagChat attaches to an injected @nolag/js-sdk client and manages a lobby for global user presence. When you call joinRoom(), it returns a ChatRoom instance that subscribes to two topics: messages for durable chat history and _typing for ephemeral typing signals. A MessageStore inside each room accumulates messages and replayed history, while a PresenceManager tracks who is currently online. The app owns the socket lifecycle; the wrapper never opens or closes it.
| Topic | Purpose | Replay |
|---|---|---|
messages | Chat messages: text, metadata, sender info | 7 days |
_typing | Typing start/stop signals (ephemeral) | None |
Installation
npm install @nolag/chat @nolag/js-sdkOne core NoLag client can back several wrapper SDKs at once, for example chat, notify, and a dashboard on a single socket, as long as each wrapper uses a distinct appName. Each wrapper attaches its handlers on construction and releases them with detach(), and never touches the socket itself. Your app owns connect() and disconnect().
Quick Start
import { NoLag } from '@nolag/js-sdk'
import { NoLagChat } from '@nolag/chat'
// The app owns one core client. In a browser, pass a token provider so the
// SDK can mint fresh short-lived client tokens from your backend.
const client = NoLag(async () => (await (await fetch('/api/nolag-token')).json()).token)
// Inject the client into the chat wrapper
const chat = new NoLagChat({ client, username: 'Alice', avatar: '/img/alice.png' })
await client.connect() // the app owns the connection
await chat.ready() // wrapper setup complete (identity, presence, rooms)
// Join a room
const room = chat.joinRoom('general')
// Send a message
room.sendMessage('Hello everyone!')
// Listen for messages
room.on('message', (msg) => {
console.log(`[${msg.username}]: ${msg.text}`)
console.log('Sent at:', new Date(msg.timestamp))
})
// Listen for typing indicators
room.on('typing', ({ users }) => {
console.log('Typing:', users.map((u) => u.username).join(', '))
})
// Trigger typing events
room.startTyping()
// ... user stops typing
room.stopTyping()
// Get online users in this room
const users = room.getUsers()
console.log('Online:', users.length)
// Teardown: the wrapper releases its handlers and topics; the app closes the
// socket (never the other way around).
chat.detach()
client.disconnect()API Reference
NoLagChat
The main class. Attaches to the injected core client, manages global user presence, and the room lifecycle.
Constructor Options
| Option | Type | Description |
|---|---|---|
client | NoLagSocket | Required. The injected core NoLag client the app owns and connects. |
username | string | Required. Display name for this user. |
avatar | string | Optional avatar URL. |
metadata | Record<string, unknown> | Optional custom user data attached to presence. |
appName | string | NoLag app for topic prefixes (default 'chat'). |
rooms | string[] | Rooms to auto-join once the wrapper is ready. |
typingTimeout | number | Ms before a typing indicator auto-clears (default 3000). |
maxMessageCache | number | Max messages kept in memory per room (default 500). |
debug | boolean | Enable wrapper debug logging (default false). |
| Method | Description |
|---|---|
ready() | Resolves once wrapper setup completed (identity, lobby, configured rooms) |
detach() | Release this wrapper's handlers and topics; terminal, never closes the socket |
joinRoom(name) | Join a chat room; returns a ChatRoom instance |
leaveRoom(name) | Leave a room and unsubscribe from its topics |
getOnlineUsers() | Return all users currently online across all rooms |
setStatus(status) | Update your own presence status (e.g. 'away', 'busy') |
updateProfile(profile) | Update display name, avatar, or other profile fields broadcast to peers |
Events: NoLagChat
| Event | Payload | Description |
|---|---|---|
connected | none | WebSocket connection established |
disconnected | reason: string | Connection closed |
reconnected | none | Reconnection successful; rooms are restored automatically |
error | error: Error | Unrecoverable error occurred |
userOnline | user: ChatUser | A user has come online in the lobby |
userOffline | user: ChatUser | A user has gone offline |
userUpdated | user: ChatUser | A user updated their profile or status |
ChatRoom
Returned by joinRoom(). Scoped to a single room; handles messaging, typing, and per-room presence.
| Method | Description |
|---|---|
sendMessage(text) | Publish a chat message to the room |
getMessages() | Return all messages currently in the local store (including replayed history) |
startTyping() | Broadcast a typing-start signal to other room members |
stopTyping() | Broadcast a typing-stop signal |
getUsers() | Return users currently present in this room |
markRead() | Mark all messages in this room as read, resetting the unread count |
Events: ChatRoom
| Event | Payload | Description |
|---|---|---|
message | ChatMessage | Incoming message from another user |
messageSent | ChatMessage | Confirmation that your own message was delivered |
userJoined | ChatUser | A user joined this room |
userLeft | ChatUser | A user left this room |
typing | { user: ChatUser, isTyping: boolean } | A user started or stopped typing |
replayStart | { count: number } | Historical message replay is beginning |
replayEnd | { replayed: number } | Historical message replay is complete |
unreadChanged | { count: number } | The unread message count for this room changed |