Skip to content

WebSocket Rooms and Namespaces — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Websocket Rooms and Namespaces. We cover key concepts, practical examples, and best practices to help you master this topic.

Rooms and namespaces allow you to organize WebSocket connections into logical groups. Rooms enable targeted message delivery to subsets of clients, while namespaces provide full channel isolation.

What You'll Learn

  • What rooms are and how to use them
  • What namespaces are and when to use them
  • Room vs namespace trade-offs
  • Implementing rooms with ws and Socket.IO
  • Broadcasting within groups

Why It Matters

Without rooms and namespaces, all WebSocket clients receive all messages. This does not scale and creates security and performance problems in multi-channel applications.

Real-World Use

A live auction platform uses rooms for each auction item. Bidders join the room for the item they are interested in and receive only relevant bid updates. The platform has thousands of rooms with millions of concurrent connections.

Flow Chart

flowchart TD
    A[Server] --> B[Namespace: /chat]
    A --> C[Namespace: /notifications]
    B --> D[Room: general]
    B --> E[Room: random]
    C --> F[Room: user-123]
    C --> G[Room: user-456]
    D --> H[Client A]
    D --> I[Client B]
    E --> J[Client C]
    F --> K[Client A]
    G --> L[Client B]

Code Examples

Example 1: Rooms with ws Library

const WebSocket = require('ws');

const server = new WebSocket.Server({ port: 8080 });
const rooms = new Map();

server.on('connection', (ws) => {
  ws.rooms = new Set();

  ws.on('message', (message) => {
    const data = JSON.parse(message);

    switch (data.type) {
      case 'join':
        ws.rooms.add(data.room);
        if (!rooms.has(data.room)) {
          rooms.set(data.room, new Set());
        }
        rooms.get(data.room).add(ws);
        broadcastToRoom(data.room, {
          type: 'notification',
          text: 'New user joined',
        });
        break;

      case 'leave':
        ws.rooms.delete(data.room);
        rooms.get(data.room)?.delete(ws);
        break;

      case 'message':
        if (data.room) {
          broadcastToRoom(data.room, {
            type: 'message',
            text: data.text,
            room: data.room,
          });
        }
        break;
    }
  });

  ws.on('close', () => {
    ws.rooms.forEach((room) => {
      rooms.get(room)?.delete(ws);
    });
  });
});

function broadcastToRoom(room, data) {
  const clients = rooms.get(room);
  if (clients) {
    clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(data));
      }
    });
  }
}

Expected output: Clients can join/leave rooms and send messages only to clients in the same room.

Example 2: Socket.IO Namespaces

const { Server } = require('socket.io');
const io = new Server(3000);

// Default namespace
io.on('connection', (socket) => {
  socket.emit('welcome', 'Connected to default namespace');
});

// Chat namespace
const chatNamespace = io.of('/chat');
chatNamespace.on('connection', (socket) => {
  console.log('Client connected to /chat:', socket.id);

  socket.on('join', (room) => {
    socket.join(room);
    chatNamespace.to(room).emit('notification', {
      text: `${socket.id} joined ${room}`,
    });
  });

  socket.on('message', ({ room, text }) => {
    chatNamespace.to(room).emit('message', {
      sender: socket.id,
      text,
    });
  });
});

// Admin namespace with authentication
const adminNamespace = io.of('/admin');
adminNamespace.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (token === 'admin-secret') {
    next();
  } else {
    next(new Error('Unauthorized'));
  }
});

adminNamespace.on('connection', (socket) => {
  socket.join('admin-room');
  adminNamespace.to('admin-room').emit('admin-event', {
    text: 'Admin connected',
  });
});

Expected output: Multiple namespaces with different behavior, including an admin namespace that requires authentication.

Example 3: Dynamic Room Management

const { Server } = require('socket.io');
const io = new Server(3000);

io.on('connection', (socket) => {
  // Join rooms based on user data
  const userId = socket.handshake.auth.userId;
  const role = socket.handshake.auth.role;

  // User-specific room
  socket.join(`user:${userId}`);

  // Role-based room
  socket.join(`role:${role}`);

  // Subscribe to document rooms
  socket.on('subscribe-document', (docId) => {
    socket.join(`doc:${docId}`);
    socket.documents = socket.documents || [];
    socket.documents.push(docId);

    io.to(`doc:${docId}`).emit('user-joined', {
      userId,
      activeUsers: getRoomUsers(`doc:${docId}`),
    });
  });

  socket.on('unsubscribe-document', (docId) => {
    socket.leave(`doc:${docId}`);
  });

  // Room statistics
  socket.on('room-stats', (room) => {
    const clients = io.sockets.adapter.rooms.get(room);
    socket.emit('room-stats', {
      room,
      users: clients ? clients.size : 0,
    });
  });

  // Cleanup on disconnect
  socket.on('disconnect', () => {
    if (socket.documents) {
      socket.documents.forEach((docId) => {
        io.to(`doc:${docId}`).emit('user-left', {
          userId,
        });
      });
    }
  });
});

function getRoomUsers(room) {
  const clients = io.sockets.adapter.rooms.get(room);
  if (!clients) return [];
  return Array.from(clients).map((id) => {
    return io.sockets.sockets.get(id)?.handshake.auth.userId;
  }).filter(Boolean);
}

Expected output: Dynamic room management based on user attributes, document subscriptions, and automatic cleanup on disconnect.

Common Mistakes

Mistake Explanation
Not cleaning up rooms on disconnect Clients that disconnect without leaving rooms leave stale references
Using too many namespaces Namespaces create separate channels; use rooms for fine-grained grouping within a namespace
Broadcasting to all when room-specific is needed Send messages to specific rooms, not global broadcast, to avoid overwhelming clients
Ignoring room member limits Some implementations have limits on room membership; know your platform limits
Mixing room concepts between libraries Room implementation differs between ws and Socket.IO; do not mix APIs

Practice Questions

  1. What is the difference between a room and a namespace?
  2. How do you implement rooms with the ws library?
  3. How does Socket.IO handle room membership tracking?
  4. When should you use namespaces vs rooms?
  5. How do you broadcast to a specific room in Socket.IO?

Challenge

Build a multi-tenant SaaS application with Socket.IO where each tenant has its own namespace. Within each namespace, there are rooms for projects, team communication, and notifications. Implement tenant isolation so messages never leak between tenants.

FAQ

How many rooms can a Socket.IO server handle?

Socket.IO can handle thousands of rooms. Each room is a Set in memory. Memory usage depends on the number of connections per room.

Can a client be in multiple rooms?

Yes, a client can join multiple rooms simultaneously. Use socket.join() for each room.

How do rooms work with multiple servers?

When using the Redis adapter, rooms work across servers. The adapter synchronizes room membership across all instances.

What is the default namespace in Socket.IO?

The default namespace is /. All connections go to the default namespace unless explicitly routed to a custom namespace.

Can I dynamically create namespaces?

Yes, you can create namespaces dynamically with io.of('/dynamic-name'). Namespaces persist once created.

How do I list all rooms on the server?

Use io.sockets.adapter.rooms to access the Map of all active rooms and their members.

Mini Project

Build a multi-channel chat application with Socket.IO namespaces for different departments (engineering, sales, support) and rooms for specific topics within each department. Include the ability to join, leave, and list active rooms.

What's Next

Learn about WebSocket broadcasting patterns

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro