Intermediate
35 min
Full Guide
Socket.io - Real-Time Library
Master Socket.io for building robust real-time applications with fallbacks
What is Socket.io?
Socket.io is the most popular real-time communication library for JavaScript. It provides a robust abstraction over WebSocket with automatic fallbacks, reconnection, rooms, namespaces, and many other features that make building real-time apps much easier.
While you could build everything with raw WebSockets, Socket.io handles the edge cases, connection management, and provides a cleaner API—especially for complex applications.
🔧 Socket.io Features
Connection
- • Automatic reconnection with backoff
- • Transport fallbacks (WebSocket → polling)
- • Connection state management
- • Multiplexing over single connection
Organization
- • Namespaces for separation
- • Rooms for grouping clients
- • Broadcast to specific groups
- • Private messaging
Reliability
- • Acknowledgments (confirm receipt)
- • Binary data support
- • Packet buffering during disconnection
- • Heartbeat mechanism
Developer Experience
- • TypeScript support
- • Event-based API
- • Middleware support
- • Debug mode
Basic Setup
// Install: npm install socket.io socket.io-client
// ============ SERVER (server.js) ============
const { Server } = require('socket.io');
const http = require('http');
const httpServer = http.createServer();
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST']
}
});
io.on('connection', (socket) => {
console.log('Client connected:', socket.id);
// Listen for events
socket.on('chat message', (msg) => {
console.log('Message:', msg);
// Broadcast to all clients
io.emit('chat message', msg);
});
socket.on('disconnect', (reason) => {
console.log('Client disconnected:', reason);
});
});
httpServer.listen(3001, () => {
console.log('Socket.io server running on port 3001');
});
// ============ CLIENT (client.js) ============
import { io } from 'socket.io-client';
const socket = io('http://localhost:3001');
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('chat message', (msg) => {
console.log('Received:', msg);
});
// Send a message
socket.emit('chat message', 'Hello, everyone!');
Events and Acknowledgments
// Sending events with various data types
socket.emit('message', 'simple string');
socket.emit('user-data', { name: 'John', age: 30 });
socket.emit('numbers', 1, 2, 3);
socket.emit('mixed', 'text', { obj: true }, [1, 2, 3]);
// Acknowledgments - confirm message was received
// Client sends, waits for server confirmation
socket.emit('save-item', { id: 1, name: 'Test' }, (response) => {
if (response.success) {
console.log('Item saved!', response.id);
} else {
console.log('Failed:', response.error);
}
});
// Server handles and responds
socket.on('save-item', (item, callback) => {
try {
const saved = database.save(item);
callback({ success: true, id: saved.id });
} catch (error) {
callback({ success: false, error: error.message });
}
});
// Timeout for acknowledgments (v4.4+)
socket.timeout(5000).emit('slow-operation', data, (err, response) => {
if (err) {
// Server didn't respond in 5 seconds
console.log('Timeout!');
} else {
console.log('Response:', response);
}
});
Rooms - Grouping Clients
// Rooms allow you to broadcast to subsets of clients
io.on('connection', (socket) => {
// Join a room
socket.on('join-room', (roomName) => {
socket.join(roomName);
console.log(`${socket.id} joined room: ${roomName}`);
// Notify room members
socket.to(roomName).emit('user-joined', {
userId: socket.id,
room: roomName
});
});
// Leave a room
socket.on('leave-room', (roomName) => {
socket.leave(roomName);
socket.to(roomName).emit('user-left', { userId: socket.id });
});
// Send message to specific room
socket.on('room-message', ({ room, message }) => {
// Send to everyone in room INCLUDING sender
io.to(room).emit('message', {
from: socket.id,
text: message
});
// OR: Send to everyone EXCEPT sender
socket.to(room).emit('message', {
from: socket.id,
text: message
});
});
// Get rooms a socket is in
console.log('Socket rooms:', socket.rooms); // Set { socket.id, 'room1', 'room2' }
// Get all sockets in a room
const socketsInRoom = await io.in('room1').fetchSockets();
console.log('Users in room1:', socketsInRoom.length);
});
// Broadcast patterns
io.emit('to-everyone'); // All connected clients
io.to('room1').emit('to-room1'); // Everyone in room1
io.to('room1').to('room2').emit('msg'); // Everyone in room1 OR room2
socket.to('room1').emit('except-sender'); // Room1 except sender
socket.broadcast.emit('except-sender'); // Everyone except sender
Namespaces - Separate Concerns
// Namespaces allow multiple apps over one connection
const io = new Server(httpServer);
// Main namespace (default: "/")
io.on('connection', (socket) => {
console.log('Main namespace:', socket.id);
});
// Chat namespace
const chatNsp = io.of('/chat');
chatNsp.on('connection', (socket) => {
console.log('Chat namespace:', socket.id);
socket.on('message', (msg) => {
chatNsp.emit('message', msg);
});
});
// Admin namespace with auth
const adminNsp = io.of('/admin');
adminNsp.use((socket, next) => {
const token = socket.handshake.auth.token;
if (validateAdminToken(token)) {
next();
} else {
next(new Error('Unauthorized'));
}
});
adminNsp.on('connection', (socket) => {
console.log('Admin connected:', socket.id);
socket.on('broadcast', (message) => {
// Broadcast to all namespaces
io.emit('announcement', message);
});
});
// Client connecting to namespaces
const chatSocket = io('http://localhost:3001/chat');
const adminSocket = io('http://localhost:3001/admin', {
auth: { token: 'admin-secret-token' }
});
chatSocket.emit('message', 'Hello chat!');
adminSocket.emit('broadcast', 'Server announcement');
Middleware
// Server middleware - runs on every connection
// Authentication middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error('Authentication required'));
}
try {
const user = verifyToken(token);
socket.user = user; // Attach user to socket
next();
} catch (error) {
next(new Error('Invalid token'));
}
});
// Logging middleware
io.use((socket, next) => {
console.log('New connection attempt from:', socket.handshake.address);
next();
});
// Rate limiting middleware
const connectionCount = new Map();
io.use((socket, next) => {
const ip = socket.handshake.address;
const count = connectionCount.get(ip) || 0;
if (count >= 10) {
return next(new Error('Too many connections'));
}
connectionCount.set(ip, count + 1);
socket.on('disconnect', () => {
connectionCount.set(ip, connectionCount.get(ip) - 1);
});
next();
});
// Event middleware (per-socket)
socket.use(([event, ...args], next) => {
console.log('Event received:', event, args);
next(); // Continue to event handler
});
// Client-side error handling
socket.on('connect_error', (error) => {
console.log('Connection error:', error.message);
// 'Authentication required' or 'Invalid token'
});
Connection Options
// Client connection options
const socket = io('http://localhost:3001', {
// Authentication
auth: {
token: 'user-jwt-token'
},
// Query parameters (visible in URL)
query: {
version: '1.0',
platform: 'web'
},
// Transport options
transports: ['websocket', 'polling'], // Try WebSocket first
upgrade: true, // Upgrade from polling to WebSocket
// Reconnection
reconnection: true, // Enable auto-reconnect
reconnectionAttempts: 5, // Max attempts
reconnectionDelay: 1000, // Initial delay
reconnectionDelayMax: 5000, // Max delay
randomizationFactor: 0.5, // Randomize delay
// Timeouts
timeout: 20000, // Connection timeout
// Other
autoConnect: true, // Connect immediately
forceNew: false, // Reuse existing connection
multiplex: true // Multiple connections share socket
});
// Server options
const io = new Server(httpServer, {
// CORS
cors: {
origin: ['http://localhost:3000', 'https://myapp.com'],
methods: ['GET', 'POST'],
credentials: true
},
// Transport
transports: ['polling', 'websocket'],
allowUpgrades: true,
// Limits
maxHttpBufferSize: 1e6, // 1MB max message size
pingTimeout: 20000,
pingInterval: 25000,
// Path
path: '/socket.io/' // Default path
});
Complete Chat Application
// Full-featured chat server
const { Server } = require('socket.io');
const http = require('http');
const server = http.createServer();
const io = new Server(server, {
cors: { origin: '*' }
});
// Store user data
const users = new Map();
io.on('connection', (socket) => {
console.log('Connected:', socket.id);
// User joins with username
socket.on('join', ({ username, room }) => {
socket.username = username;
socket.room = room;
users.set(socket.id, { username, room });
socket.join(room);
// Welcome message to user
socket.emit('message', {
type: 'system',
text: `Welcome to ${room}!`
});
// Announce to room
socket.to(room).emit('message', {
type: 'system',
text: `${username} joined the room`
});
// Send room user list
updateRoomUsers(room);
});
// Handle chat messages
socket.on('message', (text) => {
io.to(socket.room).emit('message', {
type: 'chat',
user: socket.username,
text: text,
timestamp: Date.now()
});
});
// Typing indicator
socket.on('typing', () => {
socket.to(socket.room).emit('typing', {
user: socket.username
});
});
socket.on('stop-typing', () => {
socket.to(socket.room).emit('stop-typing', {
user: socket.username
});
});
// Private message
socket.on('private-message', ({ to, text }) => {
io.to(to).emit('private-message', {
from: socket.username,
text: text
});
});
// Disconnect
socket.on('disconnect', () => {
const user = users.get(socket.id);
if (user) {
io.to(user.room).emit('message', {
type: 'system',
text: `${user.username} left the room`
});
users.delete(socket.id);
updateRoomUsers(user.room);
}
});
});
async function updateRoomUsers(room) {
const sockets = await io.in(room).fetchSockets();
const userList = sockets.map(s => ({
id: s.id,
username: s.username
}));
io.to(room).emit('room-users', userList);
}
server.listen(3001);
React Client Integration
// React hook for Socket.io
import { useEffect, useState, useCallback } from 'react';
import { io, Socket } from 'socket.io-client';
function useSocket(url: string) {
const [socket, setSocket] = useState(null);
const [connected, setConnected] = useState(false);
useEffect(() => {
const newSocket = io(url, {
auth: { token: getAuthToken() }
});
newSocket.on('connect', () => setConnected(true));
newSocket.on('disconnect', () => setConnected(false));
setSocket(newSocket);
return () => {
newSocket.close();
};
}, [url]);
return { socket, connected };
}
// Chat component
function Chat() {
const { socket, connected } = useSocket('http://localhost:3001');
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
useEffect(() => {
if (!socket) return;
socket.on('message', (msg) => {
setMessages(prev => [...prev, msg]);
});
socket.emit('join', { username: 'User', room: 'general' });
return () => {
socket.off('message');
};
}, [socket]);
const sendMessage = useCallback(() => {
if (socket && input.trim()) {
socket.emit('message', input);
setInput('');
}
}, [socket, input]);
return (
{connected ? '🟢 Connected' : '🔴 Disconnected'}
{messages.map((msg, i) => (
{msg.user}: {msg.text}
))}
setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
);
}
💡 Socket.io Best Practices
- ✓ Use rooms for grouping related clients
- ✓ Use namespaces to separate different parts of your app
- ✓ Always use acknowledgments for important messages
- ✓ Implement authentication via middleware
- ✓ Handle disconnection gracefully
- ✓ Use TypeScript for type-safe events
- ✓ Enable debug mode in development:
localStorage.debug = 'socket.io-client:*'