Members (Presence)
Beta
This feature is in beta. Core behavior is stable, but some APIs or configuration may change before general availability.
room.members is the presence layer of Room.
It tells you who is currently in the room and lets each member publish lightweight ephemeral state such as typing, cursor position, or hand raise status.
Client Surface
Assume room is an authenticated room client created with client.room(...).
- JavaScript
- Dart/Flutter
- Swift
- Kotlin
- Java
- C#/Unity
- C++/Unreal
room.members.onSync((members) => renderRoster(members));
room.members.onJoin((member) => notifyJoin(member));
room.members.onLeave((member, reason) => notifyLeave(member, reason));
await room.members.setState({ typing: true, cursor: { x: 10, y: 20 } });
await room.members.clearState();
room.members.onSync((members) => renderRoster(members));
room.members.onJoin((member) => notifyJoin(member));
room.members.onLeave((member, reason) => notifyLeave(member, reason));
await room.members.setState({'typing': true, 'cursor': {'x': 10, 'y': 20}});
await room.members.clearState();
room.members.onSync { members in renderRoster(members) }
room.members.onJoin { member in notifyJoin(member) }
room.members.onLeave { member, reason in notifyLeave(member, reason) }
try await room.members.setState(["typing": true, "cursor": ["x": 10, "y": 20]])
try await room.members.clearState()
room.members.onSync { members -> renderRoster(members) }
room.members.onJoin { member -> notifyJoin(member) }
room.members.onLeave { member, reason -> notifyLeave(member, reason) }
room.members.setState(mapOf("typing" to true, "cursor" to mapOf("x" to 10, "y" to 20)))
room.members.clearState()
room.members.onSync(members -> renderRoster(members));
room.members.onJoin(member -> notifyJoin(member));
room.members.onLeave((member, reason) -> notifyLeave(member, reason));
room.members.setState(Map.of("typing", true, "cursor", Map.of("x", 10, "y", 20))).join();
room.members.clearState().join();
room.Members.OnSync(members => RenderRoster(members));
room.Members.OnJoin(member => NotifyJoin(member));
room.Members.OnLeave((member, reason) => NotifyLeave(member, reason));
await room.Members.SetState(new Dictionary<string, object?> { ["typing"] = true });
await room.Members.ClearState();
room->members.on_sync([](const json& members) { render_roster(members); });
room->members.on_join([](const json& member) { notify_join(member); });
room->members.on_leave([](const json& member, const std::string& reason) { notify_leave(member, reason); });
room->members.set_state({{"typing", true}}, []() {}, [](const std::string&) {});
room->members.clear_state([]() {}, [](const std::string&) {});
The main client APIs are:
room.members.list()room.members.onSync(...)room.members.onJoin(...)room.members.onLeave(...)room.members.setState(...)room.members.clearState()room.members.onStateChange(...)
Member Semantics
- A public member is user-oriented, not raw-connection-oriented.
connectionIdstill exists when needed for diagnostics or metadata.connectionCountlets you distinguish one user on multiple tabs or devices.
Typical leave reasons are:
leavedisconnectkicked
Server Hooks
Room exposes member-specific hooks so you can mirror presence into metadata or analytics without mixing it into authoritative state.
rooms: {
collab: {
hooks: {
members: {
onJoin: (member, room) => {
room.setMetadata({ ...room.getMetadata(), lastMemberJoin: member.memberId });
},
onStateChange: (member, state, room) => {
room.setMetadata({
...room.getMetadata(),
lastPresenceUpdate: { memberId: member.memberId, state },
});
},
},
},
},
}
Members vs State
- Use State for source-of-truth gameplay or collaborative data.
- Use
membersfor ephemeral presence information that should disappear when users leave.
Session Interaction
Reconnect behavior affects membership:
- a disconnect can temporarily keep a member in the room during the reconnect grace period
hooks.session.onReconnect(...)andhooks.session.onDisconnectTimeout(...)let you observe that lifecycle
See Advanced for reconnect timing details.