React Realtime Hooks (totalum-sdk/react)
import … from 'totalum-sdk/react' — or the same namespace on totalum from 'totalum-sdk'. Install, configuration and errors: Totalum SDK.
The browser side of realtime rooms for React client components: useRealtimeChannel(room) connects to a room,
keeps the messages received, shows who else is present, and reconnects by itself (every deploy drops sockets). The page
never holds a key: it asks your own server route for a ticket (default POST /api/realtime/ticket), which calls
totalum.realtime.ticket. RealtimeChannel is the same thing without React.
'use client';
import { useRealtimeChannel } from 'totalum-sdk/react';
const { messages, publish } = useRealtimeChannel('chat:team-1');| Method or type | What it does |
|---|---|
useRealtimeChannel | Joins a realtime room from a React client component and re-renders on every message: status, the messages received since it opened, others (with presence), and publish(data, event?). |
RealtimeChannel | One room over one socket, without React (Vue, Svelte, plain scripts): connect(), read getSnapshot(), listen with subscribe(), send with publish(), close() when done. |
RealtimeChannelOptions | How useRealtimeChannel / RealtimeChannel get their ticket, announce presence and keep messages. |
RealtimeMessage | One message received in a room. |
RealtimePeer | Another connection present in the room (with the presence option). |
RealtimeSnapshot | The current state of a room: what useRealtimeChannel returns (plus publish), and RealtimeChannel.getSnapshot(). |
RealtimeStatus | The state of a room's connection: connecting (first ticket and socket), open, reconnecting (after a drop — every deploy drops sockets — show it as an ordinary state, not an error) or closed (after close() or unmount). |
#useRealtimeChannel
useRealtimeChannel(room: string, options?: RealtimeChannelOptions): { publish, status, messages, others }Joins a realtime room from a React client component and re-renders on every message: status, the messages
received since it opened, others (with presence), and publish(data, event?). It asks your route for a ticket
(POST /api/realtime/ticket with { room }, which calls totalum.realtime.ticket after your auth check), opens the
socket, and re-tickets and reconnects with backoff after every drop. Render status === 'reconnecting' as an
ordinary state, not an error. The options of the first render are kept; a new room opens a new connection.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
room | string | yes | The room's name (the same your ticket route allows). |
options | RealtimeChannelOptions | no | Where to get the ticket, presence data, and how many messages to keep. |
options.ticketUrl | string | no | The app's route that mints the ticket; it receives { room } and answers { url } or { data: { url } }. |
options.getTicket | (room: string) => Promise<{ url: string; }> | no | Or mint the ticket yourself. |
options.presence | M | no | Announce this connection's presence with this metadata (name, colour…); others lists everyone else's. |
options.keep | number | no | Messages kept in memory, newest last (default 100). |
options.WebSocket | { new (url: string | URL, protocols?: string | string[] | undefined): WebSocket; prototype: WebSocket; readonly CONNECTING: 0; readonly OPEN: 1; readonly CLOSING: 2; readonly CLOSED: 3; } | no | A WebSocket implementation, for tests only. Default: the browser's. |
Returns { publish, status, messages, others } — The room's snapshot (status, messages, others) and publish(data, event?), which sends to everyone in
the room (you included) and returns false when the socket is not open.
Example
'use client';
import { useRealtimeChannel } from 'totalum-sdk/react';
export function Chat({ teamId, name }: { teamId: string; name: string }) {
const { messages, others, publish, status } = useRealtimeChannel(`chat:${teamId}`, { presence: { name } });
return (
<div>
<p>{status === 'open' ? `${others.length} others online` : 'Connecting…'}</p>
{messages.map((m) => <p key={m.id}><b>{m.identity.name}</b>: {String(m.data)}</p>)}
<button onClick={() => publish('Hello!')}>Say hello</button>
</div>
);
}Result:
{
status: 'open',
messages: [{ id: '5f0c…', event: 'message', identity: { id: 'user_7', name: 'Bob' }, data: 'Hello!', ts: '2026-10-11T18:02:11.204Z' }],
others: [{ connectionId: '1b9e…', identity: { id: 'user_7', name: 'Bob' }, meta: { name: 'Bob' } }],
publish: [Function]
}#RealtimeChannel
One room over one socket, without React (Vue, Svelte, plain scripts): connect(), read getSnapshot(), listen with
subscribe(), send with publish(), close() when done. It re-tickets and reconnects by itself after a drop.
useRealtimeChannel is a thin React wrapper over it.
| Member | Type | Description |
|---|---|---|
room | string | The room's name. |
| Method | Description |
|---|---|
subscribe(listener) → () => void | Calls listener on every change of the snapshot; returns the function that stops it. |
getSnapshot() → RealtimeSnapshot | The current state: status, messages and others (a new object after each change). |
connect() → void | Fetches a ticket and opens the socket (and starts the presence heartbeat); does nothing if already connected. |
publish(data, event?) → boolean | Sends data to everyone in the room (you included); false when the socket is not open. |
close() → void | Leaves the room (announcing it to the others), closes the socket and stops reconnecting. |
Example
import { RealtimeChannel } from 'totalum-sdk/react';
const channel = new RealtimeChannel('chat:team-1', { presence: { name: 'Ana' } });
channel.subscribe(() => render(channel.getSnapshot()));
channel.connect();
channel.publish({ text: 'Hello' });
// later
channel.close();#RealtimeChannelOptions
How useRealtimeChannel / RealtimeChannel get their ticket, announce presence and keep messages.
| Name | Type | Required | Description |
|---|---|---|---|
ticketUrl | string | no | The app's route that mints the ticket; it receives { room } and answers { url } or { data: { url } }. |
getTicket | (room: string) => Promise<{ url: string; }> | no | Or mint the ticket yourself. |
presence | M | no | Announce this connection's presence with this metadata (name, colour…); others lists everyone else's. |
keep | number | no | Messages kept in memory, newest last (default 100). |
WebSocket | { new (url: string | URL, protocols?: string | string[] | undefined): WebSocket; prototype: WebSocket; readonly CONNECTING: 0; readonly OPEN: 1; readonly CLOSING: 2; readonly CLOSED: 3; } | no | A WebSocket implementation, for tests only. Default: the browser's. |
#RealtimeMessage
One message received in a room.
| Field | Type | Always present | Description |
|---|---|---|---|
id | string | yes | The message id. |
event | string | yes | The event name the sender gave (message by default). |
identity | { id, name? } | yes | Who sent it: the identity of their ticket, or { id: 'server' } for a server-side publish. |
identity.id | string | yes | The sender's user id. |
identity.name | string | no | The sender's display name, when their ticket had one. |
data | T | yes | What was sent. |
ts | string | yes | When the room received it (ISO date-time). |
#RealtimePeer
Another connection present in the room (with the presence option).
| Field | Type | Always present | Description |
|---|---|---|---|
connectionId | string | yes | One per open socket: the same user in two tabs is two peers with the same identity. |
identity | { id, name? } | yes | The peer's user, from their ticket. |
identity.id | string | yes | Their user id. |
identity.name | string | no | Their display name, when their ticket had one. |
meta | M | yes | The presence data they announced (name, colour, cursor…). |
#RealtimeSnapshot
The current state of a room: what useRealtimeChannel returns (plus publish), and RealtimeChannel.getSnapshot().
| Field | Type | Always present | Description |
|---|---|---|---|
status | RealtimeStatus | yes | The connection's state. |
messages | RealtimeMessage[] | yes | The messages received since the room opened, oldest first (at most keep). |
others | RealtimePeer[] | yes | Everyone else present (with presence); empty otherwise. |
#RealtimeStatus
The state of a room's connection: connecting (first ticket and socket), open, reconnecting (after a drop —
every deploy drops sockets — show it as an ordinary state, not an error) or closed (after close() or unmount).
| Value | Meaning |
|---|---|
'open' | Connected: messages arrive and publish works. |
'connecting' | Getting the first ticket and opening the socket. |
'reconnecting' | The socket dropped (every deploy drops sockets): getting a new ticket and reconnecting with backoff. An ordinary state, not an error. |
'closed' | Closed by close() or unmount. |
