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.
Adds real-time bidirectional communication using Socket.IO for events, rooms, and namespaces. Use when building chat apps, live updates, or multiplayer features that need instant server-client messaging.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Real-time bidirectional event-based communication library.
Install Server:
npm install socket.io
Install Client:
npm install socket.io-client
// 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');
});
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);
// 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();
}
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);
});
// 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>
);
}
// 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);
});
// 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 });
});
// Client with timeout
socket.timeout(5000).emit('request', data, (err, response) => {
if (err) {
console.error('Timeout or error');
} else {
console.log('Response:', response);
}
});
// To all connected clients (including sender)
io.emit('announcement', 'Hello everyone!');
// To all except sender
socket.broadcast.emit('user:joined', { name: 'John' });
// To specific socket ID
io.to(socketId).emit('private:message', 'Hello');
// To multiple IDs
io.to(socketId1).to(socketId2).emit('message', 'Hello');
// 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
});
// 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 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]);
// 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');
adminNamespace.use((socket, next) => {
const token = socket.handshake.auth.token;
if (isValidAdminToken(token)) {
next();
} else {
next(new Error('Unauthorized'));
}
});
// 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);
});
// Client handles errors
socket.on('connect_error', (error) => {
if (error.message === 'Authentication error') {
// Redirect to login
}
});
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()));
}
});
});
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>
);
}
| 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 |
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.