socket-io
Builds real-time applications with Socket.IO including events, rooms, namespaces, and acknowledgements. Use when implementing real-time features, chat applications, live updates, or multiplayer functionality.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Socket.IO
Real-time bidirectional event-based communication library.
Quick Start
Install Server:
npm install socket.io
Install Client:
npm install socket.io-client
Server Setup
Basic Server (Node.js)
// server.ts
import { createServer } from 'http';
import { Server } from 'socket.io';
const httpServer = createServer();
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
});
io.on('connection', (socket) => {
console.log('Client connected:', socket.id);
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
});
});
httpServer.listen(3001, () => {
console.log('Socket.IO server running on port 3001');
});
Express Integration
import express from 'express';
import { createServer } from 'http';
import { Server } from 'socket.io';
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: 'http://localhost:3000',
},
});
app.get('/', (req, res) => {
res.send('Hello World');
});
io.on('connection', (socket) => {
console.log('Connected:', socket.id);
});
httpServer.listen(3001);
Next.js API Route
// pages/api/socket.ts (Pages Router)
import { Server } from 'socket.io';
import type { NextApiRequest, NextApiResponse } from 'next';
import type { Server as HTTPServer } from 'http';
import type { Socket as NetSocket } from 'net';
interface SocketServer extends HTTPServer {
io?: Server;
}
interface SocketWithIO extends NetSocket {
server: SocketServer;
}
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (!(res.socket as SocketWithIO).server.io) {
const io = new Server((res.socket as SocketWithIO).server, {
path: '/api/socket',
addTrailingSlash: false,
});
io.on('connection', (socket) => {
console.log('Connected:', socket.id);
socket.on('message', (data) => {
io.emit('message', data);
});
});
(res.socket as SocketWithIO).server.io = io;
}
res.end();
}
Client Setup
Browser Client
import { io } from 'socket.io-client';
const socket = io('http://localhost:3001');
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('disconnect', () => {
console.log('Disconnected');
});
socket.on('connect_error', (error) => {
console.error('Connection error:', error);
});
React Hook
// hooks/useSocket.ts
import { useEffect, useState } from 'react';
import { io, Socket } from 'socket.io-client';
export function useSocket(url: string) {
const [socket, setSocket] = useState<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const socketInstance = io(url);
socketInstance.on('connect', () => {
setIsConnected(true);
});
socketInstance.on('disconnect', () => {
setIsConnected(false);
});
setSocket(socketInstance);
return () => {
socketInstance.disconnect();
};
}, [url]);
return { socket, isConnected };
}
// components/Chat.tsx
function Chat() {
const { socket, isConnected } = useSocket('http://localhost:3001');
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
if (!socket) return;
socket.on('message', (data: string) => {
setMessages((prev) => [...prev, data]);
});
return () => {
socket.off('message');
};
}, [socket]);
const sendMessage = (text: string) => {
socket?.emit('message', text);
};
return (
<div>
<p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
<ul>
{messages.map((msg, i) => (
<li key={i}>{msg}</li>
))}
</ul>
</div>
);
}
Events
Emit and Listen
// Server
io.on('connection', (socket) => {
// Receive from client
socket.on('chat:message', (data) => {
console.log('Message:', data);
});
// Send to client
socket.emit('chat:message', { text: 'Hello from server' });
});
// Client
socket.emit('chat:message', { text: 'Hello from client' });
socket.on('chat:message', (data) => {
console.log('Message:', data);
});
Acknowledgements
// Client sends with callback
socket.emit('save:data', { id: 1, name: 'Test' }, (response) => {
console.log('Server response:', response);
});
// Server handles with callback
socket.on('save:data', (data, callback) => {
// Process data
const result = saveToDatabase(data);
callback({ success: true, id: result.id });
});
With Timeout
// Client with timeout
socket.timeout(5000).emit('request', data, (err, response) => {
if (err) {
console.error('Timeout or error');
} else {
console.log('Response:', response);
}
});
Broadcasting
To All Clients
// To all connected clients (including sender)
io.emit('announcement', 'Hello everyone!');
// To all except sender
socket.broadcast.emit('user:joined', { name: 'John' });
To Specific Clients
// To specific socket ID
io.to(socketId).emit('private:message', 'Hello');
// To multiple IDs
io.to(socketId1).to(socketId2).emit('message', 'Hello');
Rooms
Join and Leave
// Server
socket.on('room:join', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('user:joined', { userId: socket.id });
});
socket.on('room:leave', (roomId) => {
socket.leave(roomId);
socket.to(roomId).emit('user:left', { userId: socket.id });
});
socket.on('disconnect', () => {
// Automatically leaves all rooms
});
Send to Room
// To everyone in room (except sender)
socket.to('room-1').emit('chat:message', { text: 'Hello room!' });
// To everyone in room (including sender)
io.in('room-1').emit('announcement', 'Room announcement');
// To everyone in multiple rooms
io.to('room-1').to('room-2').emit('message', 'Hello rooms!');
Get Room Info
// Get sockets in room
const sockets = await io.in('room-1').fetchSockets();
console.log('Users in room:', sockets.length);
// Get rooms a socket is in
const rooms = socket.rooms;
console.log('Socket rooms:', [...rooms]);
Namespaces
Create Namespace
// Server
const chatNamespace = io.of('/chat');
const adminNamespace = io.of('/admin');
chatNamespace.on('connection', (socket) => {
console.log('Chat connected:', socket.id);
});
adminNamespace.on('connection', (socket) => {
console.log('Admin connected:', socket.id);
});
// Client
const chatSocket = io('http://localhost:3001/chat');
const adminSocket = io('http://localhost:3001/admin');
Namespace Middleware
adminNamespace.use((socket, next) => {
const token = socket.handshake.auth.token;
if (isValidAdminToken(token)) {
next();
} else {
next(new Error('Unauthorized'));
}
});
Middleware
Server Middleware
// Global middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (isValidToken(token)) {
socket.data.user = decodeToken(token);
next();
} else {
next(new Error('Authentication error'));
}
});
// Access user data
io.on('connection', (socket) => {
console.log('User:', socket.data.user);
});
Error Handling
// Client handles errors
socket.on('connect_error', (error) => {
if (error.message === 'Authentication error') {
// Redirect to login
}
});
Chat Application Example
Server
interface User {
id: string;
name: string;
}
interface Message {
id: string;
userId: string;
userName: string;
text: string;
timestamp: Date;
}
const users = new Map<string, User>();
io.on('connection', (socket) => {
socket.on('user:join', (name: string) => {
const user = { id: socket.id, name };
users.set(socket.id, user);
socket.broadcast.emit('user:joined', user);
io.emit('users:list', Array.from(users.values()));
});
socket.on('message:send', (text: string) => {
const user = users.get(socket.id);
if (!user) return;
const message: Message = {
id: crypto.randomUUID(),
userId: user.id,
userName: user.name,
text,
timestamp: new Date(),
};
io.emit('message:new', message);
});
socket.on('typing:start', () => {
const user = users.get(socket.id);
if (user) {
socket.broadcast.emit('user:typing', user);
}
});
socket.on('typing:stop', () => {
socket.broadcast.emit('user:stopped-typing', socket.id);
});
socket.on('disconnect', () => {
const user = users.get(socket.id);
if (user) {
users.delete(socket.id);
io.emit('user:left', user);
io.emit('users:list', Array.from(users.values()));
}
});
});
Client
function ChatApp() {
const { socket, isConnected } = useSocket('http://localhost:3001');
const [messages, setMessages] = useState<Message[]>([]);
const [users, setUsers] = useState<User[]>([]);
const [typingUsers, setTypingUsers] = useState<User[]>([]);
useEffect(() => {
if (!socket) return;
socket.on('message:new', (message: Message) => {
setMessages((prev) => [...prev, message]);
});
socket.on('users:list', (userList: User[]) => {
setUsers(userList);
});
socket.on('user:typing', (user: User) => {
setTypingUsers((prev) => [...prev, user]);
});
socket.on('user:stopped-typing', (userId: string) => {
setTypingUsers((prev) => prev.filter((u) => u.id !== userId));
});
return () => {
socket.off('message:new');
socket.off('users:list');
socket.off('user:typing');
socket.off('user:stopped-typing');
};
}, [socket]);
const sendMessage = (text: string) => {
socket?.emit('message:send', text);
};
return (
<div>
<UserList users={users} />
<MessageList messages={messages} />
{typingUsers.length > 0 && (
<p>{typingUsers.map((u) => u.name).join(', ')} typing...</p>
)}
<MessageInput
onSend={sendMessage}
onTyping={() => socket?.emit('typing:start')}
onStopTyping={() => socket?.emit('typing:stop')}
/>
</div>
);
}
Best Practices
- Use namespaces - Separate concerns (chat, notifications, etc.)
- Use rooms for groups - Efficient broadcasting
- Implement reconnection - Handle connection drops
- Add authentication - Use middleware
- Handle errors - Catch and log errors
Common Mistakes
| Mistake | Fix |
|---|---|
| Missing CORS config | Add cors option to server |
| Not cleaning up listeners | Remove listeners in cleanup |
| Blocking event loop | Use async handlers |
| Missing error handling | Add connect_error listener |
| Memory leaks | Clean up on disconnect |
Reference Files
- references/events.md - Event patterns
- references/scaling.md - Redis adapter
- references/security.md - Authentication
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-01-28
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!