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