Once you've mastered the basics of Socket.IO, you can add features that dramatically improve the user experience. Let's explore three advanced concepts: presence (who's online), typing indicators, and the automatic reconnection magic of Socket.IO.
1. Building a Presence System ("Who's Online")
A presence system shows users a list of who else is currently connected to the application.
The core logic is to maintain a list of connected users on the server and broadcast this list whenever it changes (a user joins or leaves).
Server-Side Implementation
We'll store user data in memory. Note: For a multi-server setup, you'd need a shared store like Redis.
JavaScript
// server.js
// Use a Map to store socket IDs and usernames
const connectedUsers = new Map();
io.on('connection', (socket) => {
// 1. When a user connects, add them to the list
socket.on('user-joined', (username) => {
connectedUsers.set(socket.id, username);
console.log(`${username} joined.`);
// 2. Broadcast the updated list to everyone
io.emit('user-list-update', Array.from(connectedUsers.values()));
});
// 3. When a user disconnects, remove them and broadcast the update
socket.on('disconnect', () => {
const username = connectedUsers.get(socket.id);
if (username) {
connectedUsers.delete(socket.id);
console.log(`${username} left.`);
io.emit('user-list-update', Array.from(connectedUsers.values()));
}
});
});
Client-Side Implementation
The client needs to announce itself upon joining and then listen for list updates to render the UI.
JavaScript
// client.js
const username = prompt("What's your name?");
socket.emit('user-joined', username);
// Listen for updates and render the list
const userList = document.getElementById('user-list');
socket.on('user-list-update', (users) => {
userList.innerHTML = ''; // Clear the list
users.forEach(user => {
const item = document.createElement('li');
item.textContent = user;
userList.appendChild(item);
});
});
2. Implementing Typing Indicators
The "User is typing..." indicator gives chat applications a much more dynamic feel.
The idea is to emit an event when a user starts typing and another when they stop. We'll use socket.broadcast.emit() because we only want to notify other users.
Client-Side Logic
JavaScript
// client.js
const input = document.getElementById('input');
let typingTimeout;
// When the user types in the input box
input.addEventListener('input', () => {
// Let the server know we're typing
socket.emit('user-typing', true);
// Clear any existing timeout
clearTimeout(typingTimeout);
// Set a timeout to signal that we've stopped typing
typingTimeout = setTimeout(() => {
socket.emit('user-typing', false);
}, 2000); // 2 seconds of inactivity
});
Server-Side Logic
The server simply relays this typing status to other clients.
JavaScript
// server.js
socket.on('user-typing', (isTyping) => {
const username = connectedUsers.get(socket.id);
// Broadcast to everyone else
socket.broadcast.emit('user-typing-update', {
username: username,
isTyping: isTyping,
});
});
The receiving clients would then listen for the user-typing-update event and show/hide a "username is typing..." message in their UI.
3. Understanding Reconnection Logic
One of Socket.IO's best features is that it handles temporary network disconnects automatically. If a user's Wi-Fi drops for a moment, the client will try to reconnect on its own.
You can hook into this process to provide feedback to your users.
- disconnect: Fires when the connection is lost.
- reconnect_attempt: Fires each time the client tries to reconnect.
- reconnect: Fires upon a successful reconnection.
Client-Side UI Feedback
JavaScript
// client.js
const statusIndicator = document.getElementById('status');
socket.on('disconnect', (reason) => {
statusIndicator.textContent = 'Disconnected. Attempting to reconnect...';
// The 'reason' tells you why the disconnect happened
console.log(`Disconnected: ${reason}`);
});
socket.on('reconnect', (attemptNumber) => {
statusIndicator.textContent = 'Connected!';
console.log(`Reconnected after ${attemptNumber} attempts.`);
// Re-announce user to repopulate presence system
socket.emit('user-joined', username);
});
With Socket.IO v4+, the server can even buffer events that were emitted while the client was disconnected and deliver them upon reconnection, ensuring no messages are lost