The Event Model
Socket.IO replaces raw WebSocket message handling with a named-event abstraction. Instead of parsing a single generic 'message' event and branching on its contents, both the server and client call socket.emit('eventName', data) to send information and register socket.on('eventName', handler) to receive it. Under the hood, Socket.IO serializes the event name and payload into its own wire protocol (Engine.IO packets), but application code never touches that layer directly.
Cricket analogy: It's like a fielding captain calling out specific field placements ('mid-on!', 'deep square!') instead of shouting one vague instruction — each named call (event) triggers a distinct, pre-rehearsed response from the player who's listening for it.
Emitting from the server
On the server, every connected client is represented by a socket object inside the io.on('connection', (socket) => { ... }) handler. Calling socket.emit('chat message', { user, text }) sends that event only to the one client tied to that socket instance. The payload can be any JSON-serializable value — objects, arrays, strings, numbers — and Socket.IO also supports binary data such as ArrayBuffers or Buffers without extra encoding, since it uses a binary-capable parser by default.
Cricket analogy: It's like a stump microphone feed routed to only one broadcaster's earpiece rather than the whole stadium PA — socket.emit on a single socket reaches exactly one connected client, not everyone.
Listening on the client
On the client, socket.on('eventName', callback) registers a handler that fires every time the server (or, in peer broadcasts, another client relayed through the server) emits that event name to this socket. Multiple .on() calls for the same event name all fire, in registration order, so you can attach several independent listeners — for example, one that updates UI state and another that logs analytics — without them interfering. Handlers can be removed with socket.off('eventName', callback) or socket.removeAllListeners('eventName') when a component unmounts, which matters in frameworks like React to avoid duplicate handlers after re-renders.
Cricket analogy: It's like a batter's stance being pre-set to react to a bouncer the instant it's bowled — socket.on primes the client to react automatically whenever that specific delivery (event) arrives, no polling required.
// server.js
io.on('connection', (socket) => {
console.log('client connected:', socket.id);
socket.on('chat message', (payload) => {
console.log('received:', payload.text);
socket.emit('chat message', { user: 'server', text: 'got it!' });
});
});
// client.js
import { io } from 'socket.io-client';
const socket = io('https://chat.example.com');
socket.emit('chat message', { user: 'alice', text: 'hello' });
socket.on('chat message', (payload) => {
console.log(`${payload.user}: ${payload.text}`);
});
// cleanup, e.g. in a React useEffect return
function handler(msg) { console.log(msg); }
socket.on('notice', handler);
socket.off('notice', handler);Event names are just strings — there's no schema enforced by Socket.IO itself. Many teams define shared constants (e.g. EVENTS.CHAT_MESSAGE = 'chat message') in a file imported by both client and server code to avoid typos causing silently-dropped events.
Avoid the reserved event names 'connect', 'disconnect', 'disconnecting', 'error', 'connect_error', and 'connect_timeout' for your own custom events — Socket.IO uses these internally, and overloading them can cause confusing behavior or silently swallowed handlers.
- socket.emit(event, data) sends a named event with a JSON-serializable (or binary) payload.
- socket.on(event, handler) registers a listener that fires on every matching emitted event.
- Emitting from a single server-side socket sends data to exactly one connected client.
- Multiple .on() handlers for the same event all run, in the order they were registered.
- Use socket.off() or removeAllListeners() to clean up handlers and avoid duplicates.
- Avoid reusing Socket.IO's reserved event names for custom application events.
- Sharing event-name constants between client and server prevents typo bugs.
Practice what you learned
1. What does socket.emit('chat message', data) do when called inside io.on('connection', socket => {...}) on the server?
2. If you call socket.on('update', handlerA) and later socket.on('update', handlerB) on the same client, what happens when 'update' is received?
3. Which of these event names should you avoid using for your own custom application events?
4. What is the correct way to stop a specific listener function from responding to future events?
5. What kind of payload data can socket.emit() send without manual encoding?
Was this page helpful?
You May Also Like
Acknowledgements and Callbacks
Learn how Socket.IO acknowledgements let the sender of an event know it was received and processed, turning fire-and-forget emits into request/response style calls.
Broadcasting Events
Learn how to send events to groups of clients at once using io.emit, broadcast.emit, and rooms, the mechanisms that power chat rooms, live dashboards, and multiplayer presence.
Custom Events and Payloads
Learn how to design well-structured custom event names and payload shapes in Socket.IO, including validation, versioning, and binary data handling for maintainable real-time APIs.
The Connection Lifecycle
Understand how a Socket.IO connection is established, upgraded, monitored, and torn down, including reconnection behavior and the events that mark each stage.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics