---
title: "Chat SDK"
description: "Multi-room chat with presence, typing indicators, message replay, and user mapping."
---

# @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

```bash [Terminal]
npm install @nolag/chat @nolag/js-sdk
```

One 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

```typescript [TypeScript]
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 |
