Presence systems — tracking who is online with WebSockets
presence state, connect and disconnect events, heartbeat-based presence, presence expiry TTL, broadcast presence changes, presence data structure, presence at scale limitations
Presence Is Just Connect and Disconnect Events
The simplest presence system tracks connections and disconnections:
const presence = new Map(); // userId -> { name, connectedAt } io.on('connection', (socket) => { const user = socket.data.user; // Mark online presence.set(user.id, { name: user.name, connectedAt: Date.now() }); io.emit('presence:update', { userId: user.id, status: 'online' }); socket.on('disconnect', () => { presence.delete(user.id); io.emit('presence:update', { userId: user.id, status: 'offline' }); }); // Send current presence to new joiner socket.emit('presence:snapshot', Object.fromEntries(presence)); });
Heartbeat-Based Presence for Accuracy
A user who closes a laptop without disconnecting keeps their presence entry alive. Add a heartbeat: the client sends a heartbeat event every 30 seconds. The server updates a lastSeen timestamp. A background job marks users as offline if lastSeen is older than 45 seconds. This eliminates ghost presences at the cost of a 45-second offline detection delay, which is acceptable for most applications.
