Server: Presence
Enable real-time tracking of who’s in a room. Mesh uses Redis TTLs and keyspace notifications to automatically clean up stale entries and notify subscribed clients.
Enable presence tracking
Track individual rooms or patterns:
server.trackPresence("lobby");
server.trackPresence(/^.+$/);
With options
Customize TTL or restrict visibility per connection:
server.trackPresence("admin-room", {
ttl: 60_000, // Presence entry expires after 60s of inactivity (default is 0, meaning no expiration)
guard: async (conn, roomName) => {
const meta = await server.connectionManager.getMetadata(conn);
return meta?.isAdmin === true;
},
});
How it works
- Presence entries are stored in Redis with a TTL
- TTL is refreshed while the connection is alive
- On disconnect or inactivity, TTL expires
- Mesh emits a
leave
event and notifies subscribers
Presence expiration uses Redis keyspace notifications. You can disable this via enablePresenceExpirationEvents: false
in your MeshServer
config.
This might be desirable if you want to implement a custom cleanup strategy, or if your Redis setup is very high traffic to the point of the overhead of processing keyspace notifications could impact performance. In most cases though, you should leave this enabled.
Get current presence
const ids = await server.presenceManager.getPresentConnections("lobby");
// ["conn123", "conn456"]
Optional: Disable auto-cleanup
const server = new MeshServer({
enablePresenceExpirationEvents: false,
});
With this setting, you are responsible for manually cleaning up expired presence entries.
Per-connection model
Presence is tracked per connection, not per user.
If a user opens multiple tabs or devices, each one gets a unique connection ID and separate presence state—even if all share the same userId
in metadata.
This gives you precise control and avoids assumptions about identity or sessions.
To group presence by user:
- Store
userId
in connection metadata - Resolve metadata by
connectionId
on the client withclient.getConnectionMetadata(connectionId)
- Deduplicate by
userId
in your UI
This enables user-level presence like “only one typing indicator per user” even with multiple sessions.
Presence state
Clients can publish ephemeral presence states (e.g. "typing"
, "away"
).
On the server:
- State is stored at
mesh:presence:state:{room}:{connectionId}
- It’s removed on disconnect, leave, or expiration
- States can have TTLs via
expireAfter
- When cleared, Mesh emits
{ type: "state", state: null }
to subscribers
Enrich presence with metadata
server.onConnection(async (conn) => {
await server.connectionManager.setMetadata(conn, {
userId: "user123",
username: "Alice",
avatar: "https://example.com/avatar.png",
});
});
See Client SDK → Presence for subscribing and handling updates.