Client: Presence
Track who’s online in a room, listen for join/leave events, and share ephemeral presence state (like "typing"
or "away"
).
Need user-level presence across tabs or devices? Use the createPresence utility to group connections by user ID or any other logic.
Subscribing
Subscribe to presence in a room:
const { , } = await .(
"lobby",
() => {
if (. === "join") {
.("User joined:", .);
} else if (. === "leave") {
.("User left:", .);
} else if (. === "state") {
.("State update:", ., .);
}
}
);
You’ll receive:
- A list of currently present members with metadata via
present
- Real-time
"join"
,"leave"
, and"state"
events
The present
array includes both connection IDs and their metadata:
// present structure:
[
{ id: "conn_123", metadata: { username: "alice", avatar: "..." } },
{ id: "conn_456", metadata: { username: "bob", avatar: "..." } }
]
Unsubscribe when done:
await .("lobby");
Join + subscribe in one step
Use joinRoom()
with a callback to automatically subscribe to presence:
const { , } = await .("lobby", () => {
.();
});
Both subscribePresence()
and joinRoom()
return the same present
format with metadata included.
If you omit the callback from joinRoom()
, you still get the current occupants with metadata - but you won’t be subscribed to real-time updates.
Leaving rooms
When you leave a room, any active presence subscription for that room is automatically cleaned up:
await .("lobby"); // unsubscribes from presence
You don’t need to manually call unsubscribePresence()
after leaving a room.
Publishing presence state
Send ephemeral presence state to others in the room:
await .("lobby", {
: { : "typing" },
: 8000, // optional (ms)
});
Clear it manually:
await .("lobby");
Automatic presence refresh
When the server sends a ping and the client responds with a pong, the server refreshes the presence TTL for all rooms that the client connection has joined.
You don’t need to manually track rooms or re-send updates for each room your client has joined.
Receiving presence states
Presence updates include state events:
const { , , } = await .(
"lobby",
() => {
if (. === "state") {
.(`${.} state:`, .);
}
}
);
You’ll also get a states
object with current values at the time of subscription:
{
"abc123": { status: "typing" },
"def456": { status: "away" }
}
Working with member metadata
Both methods return member information including metadata:
const { , } = await .("lobby", callback);
// or
const { , } = await .("lobby");
// Display current members
.( => {
.(`${.?.username || .} is online`);
});
For real-time updates in your presence callback, resolve metadata for new connections:
await .("lobby", async () => {
if (. === "join") {
const = await .(.);
.(`${?.username || .} joined`);
}
});
Use cases
- Online indicators with user names and avatars
- Room occupancy lists
- Typing indicators
- Game states like “ready”, “spectating”, etc.
Tips
- Both
joinRoom()
andsubscribePresence()
include member metadata automatically - Unsubscribe when no longer needed to reduce overhead
- Resubscribe after reconnecting
(or use
createPresence
, which handles this for you) - Use
expireAfter
for short-lived states like"typing"
- Debounce frequent updates to avoid spamming the network