Socket.IO Basics
What Is Socket.IO and Why Use It?
Raw WebSockets work, but they come with challenges in production:
- Older browsers or corporate firewalls may block WebSocket connections
- There is no built-in reconnection logic — if the connection drops, you rebuild it manually
- Broadcasting to groups of clients requires writing your own room management
- No built-in acknowledgement system to confirm messages were received
Socket.IO is a library that wraps WebSockets and adds all of these features. Under the hood it uses WebSockets when available, and gracefully falls back to HTTP long polling when WebSockets are blocked. For most real-time applications, Socket.IO is the right starting point.
Server Setup with Express and Socket.IO
npm install socket.io express
// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const server = createServer(app); // wrap express in an HTTP server
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000', // your frontend URL
methods: ['GET', 'POST'],
},
});
io.on('connection', (socket) => {
console.log('A user connected:', socket.id);
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
server.listen(5000, () => {
console.log('Server running on http://localhost:5000');
});
Note: you must wrap Express in Node's http.createServer to give Socket.IO access to the same HTTP server.
Client Connection
In a plain HTML file, load Socket.IO's client script from your server:
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:5000');
socket.on('connect', () => {
console.log('Connected. My ID:', socket.id);
});
socket.on('disconnect', () => {
console.log('Disconnected from server');
});
</script>
In a React or Vue project, install the client package:
npm install socket.io-client
import { io } from 'socket.io-client';
const socket = io('http://localhost:5000');
Emitting and Listening to Events
Socket.IO uses a named-event model. Both sides emit events and listen for them with on:
// --- SERVER ---
io.on('connection', (socket) => {
// Listen for 'chat-message' from this client
socket.on('chat-message', (data) => {
console.log('Message received:', data.text);
// Echo it back to the sender only
socket.emit('chat-message', { text: data.text, from: 'server-echo' });
});
});
// --- CLIENT ---
// Send a message to the server
socket.emit('chat-message', { text: 'Hello from the client!' });
// Listen for messages coming from the server
socket.on('chat-message', (data) => {
console.log('Server says:', data.text);
});
Broadcasting to All Clients
When a user sends a message in a chat app, everyone should see it — not just the sender.
io.on('connection', (socket) => {
socket.on('chat-message', (data) => {
// Send to ALL connected clients (including the sender)
io.emit('chat-message', data);
// Send to ALL connected clients EXCEPT the sender
socket.broadcast.emit('chat-message', data);
});
});
| Method | Recipients |
|---|---|
socket.emit() | Only the sender |
io.emit() | All connected clients |
socket.broadcast.emit() | All clients except the sender |
Rooms: Organising Clients into Groups
Rooms let you send events to a specific subset of clients. Think of them as private channels.
// --- SERVER ---
io.on('connection', (socket) => {
// Client joins a room
socket.on('join-room', (roomName) => {
socket.join(roomName);
console.log(`${socket.id} joined room: ${roomName}`);
// Notify everyone else in the room
socket.to(roomName).emit('user-joined', { userId: socket.id });
});
// Send a message to a specific room
socket.on('room-message', ({ room, text }) => {
io.to(room).emit('room-message', { text, from: socket.id });
});
// Leave a room
socket.on('leave-room', (roomName) => {
socket.leave(roomName);
socket.to(roomName).emit('user-left', { userId: socket.id });
});
});
// --- CLIENT ---
socket.emit('join-room', 'general');
socket.emit('room-message', { room: 'general', text: 'Hello everyone!' });
socket.on('room-message', (data) => console.log(data.text));
Acknowledgements
Socket.IO supports callbacks to confirm that a message was received and processed:
// --- CLIENT ---
socket.emit('save-message', { text: 'Important message' }, (response) => {
if (response.success) {
console.log('Message saved with ID:', response.id);
}
});
// --- SERVER ---
socket.on('save-message', async (data, callback) => {
const saved = await Message.create({ text: data.text });
callback({ success: true, id: saved._id });
});
Practice Exercise
- Create an Express + Socket.IO server.
- When a client connects, emit a
welcomeevent with a welcome message. - Listen for a
chat-messageevent and broadcast it to all other connected clients. - Implement a
join-roomevent that puts the client in a named room and broadcasts auser-joinedevent to that room. - Test with two browser tabs open at the same time.
Try it yourself
Key Takeaways
- Socket.IO wraps WebSockets with automatic reconnection, fallback to long polling, rooms, and acknowledgements.
- Wrap Express in http.createServer and pass the server to Socket.IO so both share the same port.
- Use socket.emit for the sender, io.emit for all clients, and socket.broadcast.emit for all clients except the sender.
- Rooms let you group sockets into named channels and broadcast events to specific subsets of connected clients.
- Acknowledgement callbacks confirm that a message was received and allow the receiver to send data back to the sender.
Quick Quiz
1.Why must you wrap Express in Node's http.createServer before using Socket.IO?
2.What is the difference between io.emit() and socket.broadcast.emit()?
3.What are Socket.IO rooms used for?
4.What does a Socket.IO acknowledgement callback allow you to do?
Ready to go further?
CareerEx gives you structured 12-week training, live classes every Saturday and Sunday, real tutor feedback, and a certificate. Join the next cohort.
Join CareerEx