This episode covers connection grouping in Socket.IO: the room and namespace concepts, joining and leaving rooms, targeted broadcasting patterns, and a per-room chat implementation.

In episode 8 you secured connections with authentication. Now it is time to answer a bigger question: how do you send messages to some clients, not all of them? The answer is rooms, namespaces, and broadcasting patterns.
Episode 9 covers three important Socket.IO tools: room for connection groups, namespace for separating communication domains, and broadcasting for reaching the right target. You will build a chat that supports many rooms at once, much like channels in professional messaging applications.
A room is a group of connections created on the server side. A connection can join many rooms at once, and the server can send messages only to the members of a specific room.
io.on("connection", (socket) => {
socket.on("join:room", (room) => {
socket.join(room);
socket.emit("sistem", "Kamu masuk ke room " + room);
});
socket.on("leave:room", (room) => {
socket.leave(room);
});
});socket.join(room) adds the connection to a room, and socket.leave(room) pulls it out. Rooms are created automatically when the first connection joins, and removed when empty.
A message sent to a room only reaches the members of that room. This separates conversations logically without creating a new server.
socket.to("game-1").emit("status", { pemain: 3 });The socket.to(room).emit(...) method sends to every member of the room except the sender. To include the sender, use io.to(room).emit(...).
A namespace is a separate endpoint within a single Socket.IO server. The default namespace is /, but you can create new ones such as /admin or /public.
const adminNs = io.of("/admin");
adminNs.on("connection", (socket) => {
socket.on("hapus:user", (id) => {
adminNs.emit("user:deleted", id);
});
});io.of("/admin") creates a new namespace. Connections to that namespace are isolated from other namespaces: an event emitted on /admin is not received by connections on /.
Clients connect to a namespace by adding its path to the URL.
const adminSocket = io("https://api.example.com/admin", {
auth: { token: localStorage.getItem("token") },
});The URL https://api.example.com/admin points to the admin namespace. Namespaces are often used to separate the public area, the admin area, and user-specific areas within one application.
There are several ways to broadcast a message, each with a different target:
io.emit("event", data); // semua klien
socket.emit("event", data); // hanya pengirim
socket.broadcast.emit("event", data); // semua kecuali pengirim
io.to("room-a").emit("event", data); // anggota room-aThe io.emit(...) pattern broadcasts to every connection in the namespace. Combining to() and emit() enables sending to one room, several rooms at once, or unicast to a specific connection.
Sometimes the send needs to consider a condition, for example only to users who are online or not busy.
io.to(room).emit("pesan", pesan);
socket.to(room).emit("tipe", {
nama: socket.nama,
mengetik: true,
});This pattern is commonly used for typing indicators: only room members receive the status, not the whole server.
Let us combine all the concepts into a multi-room chat server:
const { Server } = require("socket.io");
const io = new Server(server);
io.on("connection", (socket) => {
socket.on("chat:join", (data) => {
socket.join(data.room);
socket.to(data.room).emit("sistem", data.nama + " bergabung");
});
socket.on("chat:message", (data) => {
io.to(data.room).emit("chat:message", {
nama: data.nama,
teks: data.teks,
});
});
});The chat:message handler uses io.to(data.room) so the message only reaches the target room. The join and message events use the same data, so a single server can serve thousands of different rooms.
Rooms are very light on memory — a server can host tens of thousands of them. When the server is split into several instances, room synchronization requires the Redis adapter covered in episode 16.
Episode 9 completed the communication toolkit: rooms for groups, namespaces for isolated domains, and broadcast patterns tunable from as small as unicast to as large as the whole server.
Key takeaways:
In the next episode we cover message serialization & data formats: how to package text and binary messages, JSON, MessagePack, and protobuf formats, and the envelope pattern for messages that are structured and easy to keep versioned.