Channels implement the publish subscribe pattern. A message published to a channel reaches every connection subscribed to it, in order, usually within a few milliseconds.
Channels
Get a channel by name. Instances are stable, so calling get twice with the same name returns the same channel.
const channel = realtime.channels.get('chat:lobby');
Channel names are 1 to 255 characters from A-Z a-z 0-9 : - _, and must not start with a colon. Use colons for hierarchy, like chat:rooms:42, which pairs with capability patterns like chat:*. Dots are not allowed. Invalid names are rejected with error 40001.
Subscribe
Subscribe to everything on a channel, to one message name, or to a set of names. Subscribing attaches the channel automatically.
const off = channel.subscribe((message) => {
console.log(message.name, message.data);
});
channel.subscribe('typing', onTyping);
channel.subscribe(['added', 'removed'], onChange);
off();
channel.unsubscribe();
Every subscribe call returns an unsubscribe function. channel.unsubscribe() with no arguments removes all message listeners, and channel.detach() stops receiving from the channel entirely.
Publish
await channel.publish('chat', { text: 'hello' });
The first argument is the message name subscribers can filter on, the second is any JSON-serializable payload. The promise resolves once the service has accepted the message. Whether the message is also stored for history depends on the channel’s channel rule.
While the connection is down or still connecting, publishes are queued and flushed on connect (queueMessages: true is the default).
Batching
Publish an array to send several messages as one atomic batch. Subscribers still receive them as individual messages.
await channel.publish([
{ name: 'added', data: { id: 1 } },
{ name: 'added', data: { id: 2 } },
]);
Single publish calls are also batched automatically per channel and flushed on a short throttle, which keeps rapid-fire publishes cheap. Call channel.flush() to force the buffer out immediately.
Ephemeral messages
Ephemeral messages skip storage entirely: they are not written to history and never replayed, which makes them the lowest-latency option for high-frequency, disposable state like cursor positions.
await channel.publish('cursor', { x, y }, { ephemeral: true });
The live demo on the homepage moves cursors, click waves, and drawn strokes exactly this way.
The message object
Subscribers receive a message object with these fields:
| Field | Type | Meaning |
|---|---|---|
name |
string |
The message name given to publish |
data |
unknown |
The payload, decrypted when the channel has a cipher |
clientId |
string? |
Who published it, when known |
timestamp |
number |
Publish time in ms since epoch |
messageId |
string |
Unique id, usable as a history cursor |
ephemeral |
boolean? |
True for fire-and-forget messages |
Delivery guarantees
- Messages on one channel arrive in publish order.
- Publishes carry a stable client-assigned id, so resends after a reconnect are deduplicated by the service. You will not see doubles.
- The SDK detects sequence gaps after reconnects and backfills them from storage transparently.
- Ephemeral messages are at-most-once by design: if you were not connected when one was sent, it is gone.