Learn WebSocket - Rooms, Namespaces & Broadcasting Patterns
Episode 9 of 34

Learn WebSocket - Rooms, Namespaces & Broadcasting Patterns

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.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

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.

The Room Concept in Socket.IO

What Is a Room

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.

JSJoining and leaving a 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.

Toward Isolated Spaces

A message sent to a room only reaches the members of that room. This separates conversations logically without creating a new server.

JSRoom-specific broadcast
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(...).

Namespace

Separating Communication Domains

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.

JSCreating a dedicated namespace
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 /.

Connecting to a Namespace

Clients connect to a namespace by adding its path to the URL.

JSClient connecting to a namespace
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.

Broadcasting Patterns

Four Basic Patterns

There are several ways to broadcast a message, each with a different target:

JSBroadcast pattern variations
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-a

The 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.

Conditional Broadcasting

Sometimes the send needs to consider a condition, for example only to users who are online or not busy.

JSConditional broadcast
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.

Implementing a Per-Room Chat

The Room Chat Server

Let us combine all the concepts into a multi-room chat server:

JSMulti-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.

Room Scalability

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.

Closing

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:

  • Rooms group connections logically without a separate server.
  • Join and leave are done with socket.join and socket.leave.
  • Namespaces separate domains such as public and admin.
  • Broadcast patterns determine the target: everyone, everyone except the sender, or per room.
  • A multi-room chat can be built with just a few lines of handlers.

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.