PageSourceSearch

https://socket.io/assets/js/8113c506.c9e0784f.js

js socket.io collected 2026-10-01 07:34:32 UTC 15,838 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunksample_website=self.webpackChunksample_website||[]).push([[2347],{3905:(e,t,o)=>{o.d(t,{Zo:()=>m,kt:()=>k});var n=o(7294);function a(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e}function i(e,t){var o=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),o.push.apply(o,n)}return o}function r(e){for(var t=1;t<arguments.length;t++){var o=null!=arguments[t]?arguments[t]:{};t%2?i(Object(o),!0).forEach((function(t){a(e,t,o[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(o)):i(Object(o)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(o,t))}))}return e}function s(e,t){if(null==e)return{};var o,n,a=function(e,t){if(null==e)return{};var o,n,a={},i=Object.keys(e);for(n=0;n<i.length;n++)o=i[n],t.indexOf(o)>=0||(a[o]=e[o]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)o=i[n],t.indexOf(o)>=0||Object.prototype.propertyIsEnumerable.call(e,o)&&(a[o]=e[o])}return a}var l=n.createContext({}),c=function(e){var t=n.useContext(l),o=t;return e&&(o="function"==typeof e?e(t):r(r({},t),e)),o},m=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},p="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},u=n.forwardRef((function(e,t){var o=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,m=s(e,["components","mdxType","originalType","parentName"]),p=c(o),u=a,k=p["".concat(l,".").concat(u)]||p[u]||d[u]||i;return o?n.createElement(k,r(r({ref:t},m),{},{components:o})):n.createElement(k,r({ref:t},m))}));function k(e,t){var o=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=o.length,r=new Array(i);r[0]=u;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[p]="string"==typeof e?e:a,r[1]=s;for(var c=2;c<i;c++)r[c]=o[c];return n.createElement.apply(null,r)}return n.createElement.apply(null,o)}u.displayName="MDXCreateElement"},8841:(e,t,o)=>{o.r(t),o.d(t,{assets:()=>m,contentTitle:()=>l,default:()=>k,frontMatter:()=>s,metadata:()=>c,toc:()=>p});var n=o(3117),a=(o(7294),o(3905)),i=o(941),r=o(4996);const s={title:"Rooms",sidebar_position:4,slug:"/rooms/",alias:["/docs/rooms/","/docs/rooms-and-namespaces/"]},l=void 0,c={unversionedId:"categories/Events/rooms",id:"version-3.x/categories/Events/rooms",title:"Rooms",description:"A room is an arbitrary channel that sockets can join and leave. It can be used to broadcast events to a subset of clients:",source:"@site/versioned_docs/version-3.x/categories/04-Events/rooms.md",sourceDirName:"categories/04-Events",slug:"/rooms/",permalink:"/docs/v3/rooms/",draft:!1,editUrl:"https://github.com/socketio/socket.io-website/edit/main/versioned_docs/version-3.x/categories/04-Events/rooms.md",tags:[],version:"3.x",lastUpdatedAt:1784643894,formattedLastUpdatedAt:"Jul 21, 2026",sidebarPosition:4,frontMatter:{title:"Rooms",sidebar_position:4,slug:"/rooms/",alias:["/docs/rooms/","/docs/rooms-and-namespaces/"]},sidebar:"version-3.x/sidebar",previous:{title:"Broadcasting events",permalink:"/docs/v3/broadcasting-events/"},next:{title:"Emit cheatsheet",permalink:"/docs/v3/emit-cheatsheet/"}},m={},p=[{value:"Joining and leaving",id:"joining-and-leaving",level:2},{value:"Default room",id:"default-room",level:2},{value:"Sample use cases",id:"sample-use-cases",level:2},{value:"Usage with asynchronous code",id:"usage-with-asynchronous-code",level:2},{value:"Disconnection",id:"disconnection",level:2},{value:"With multiple Socket.IO servers",id:"with-multiple-socketio-servers",level:2},{value:"Implementation details",id:"implementation-details",level:2},{value:"Room events",id:"room-events",level:2}],d={toc:p},u="wrapper";function k(e){let{components:t,...o}=e;return(0,a.kt)(u,(0,n.Z)({},d,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"A ",(0,a.kt)("em",{parentName:"p"},"room")," is an arbitrary channel that sockets can ",(0,a.kt)("inlineCode",{parentName:"p"},"join")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"leave"),". It can be used to broadcast events to a subset of clients:"),(0,a.kt)(i.Z,{alt:"Broadcasting to all clients in a room",sources:{light:(0,r.Z)("/images/rooms.png"),dark:(0,r.Z)("/images/rooms-dark.png")},mdxType:"ThemedImage"}),(0,a.kt)("admonition",{type:"info"},(0,a.kt)("p",{parentName:"admonition"},"Please note that rooms are a ",(0,a.kt)("strong",{parentName:"p"},"server-only")," concept (i.e. the client does not have access to the list of rooms it has joined).")),(0,a.kt)("h2",{id:"joining-and-leaving"},"Joining and leaving"),(0,a.kt)("p",null,"You can call ",(0,a.kt)("inlineCode",{parentName:"p"},"join")," to subscribe the socket to a given channel:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", socket => {\n  socket.join("some room");\n});\n')),(0,a.kt)("p",null,"And then simply use ",(0,a.kt)("inlineCode",{parentName:"p"},"to")," or ",(0,a.kt)("inlineCode",{parentName:"p"},"in")," (they are the same) when broadcasting or emitting:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"}
1,'io.to("some room").emit("some event");\n')),(0,a.kt)("p",null,"You can emit to several rooms at the same time:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.to("room1").to("room2").to("room3").emit("some event");\n')),(0,a.kt)("p",null,"In that case, a ",(0,a.kt)("a",{href:"https://en.wikipedia.org/wiki/Union_(set_theory)"},"union")," is performed: every socket that is at least in one of the rooms will get the event ",(0,a.kt)("strong",{parentName:"p"},"once")," (even if the socket is in two or more rooms)."),(0,a.kt)("p",null,"You can also broadcast to a room from a given socket:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", function(socket){\n  socket.to("some room").emit("some event");\n});\n')),(0,a.kt)("p",null,"In that case, every socket in the room ",(0,a.kt)("strong",{parentName:"p"},"excluding")," the sender will get the event."),(0,a.kt)(i.Z,{alt:"Broadcasting to all clients in a room excepting the sender",sources:{light:(0,r.Z)("/images/rooms2.png"),dark:(0,r.Z)("/images/rooms2-dark.png")},mdxType:"ThemedImage"}),(0,a.kt)("p",null,"To leave a channel you call ",(0,a.kt)("inlineCode",{parentName:"p"},"leave")," in the same fashion as ",(0,a.kt)("inlineCode",{parentName:"p"},"join"),"."),(0,a.kt)("h2",{id:"default-room"},"Default room"),(0,a.kt)("p",null,"Each ",(0,a.kt)("inlineCode",{parentName:"p"},"Socket")," in Socket.IO is identified by a random, unguessable, unique identifier ",(0,a.kt)("a",{parentName:"p",href:"/docs/v3/server-socket-instance/#Socket-id"},"Socket#id"),". For your convenience, each socket automatically joins a room identified by its own id."),(0,a.kt)("p",null,"This makes it easy to implement private messages:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", socket => {\n  socket.on("private message", (anotherSocketId, msg) => {\n    socket.to(anotherSocketId).emit("private message", socket.id, msg);\n  });\n});\n')),(0,a.kt)("h2",{id:"sample-use-cases"},"Sample use cases"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"broadcast data to each device / tab of a given user")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", async (socket) => {\n  const userId = await computeUserIdFromHeaders(socket);\n\n  socket.join(userId);\n\n  // and then later\n  io.to(userId).emit("hi");\n});\n')),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"send notifications about a given entity")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", async (socket) => {\n  const projects = await fetchProjects(socket);\n\n  projects.forEach(project => socket.join("project:" + project.id));\n\n  // and then later\n  io.to("project:4321").emit("project updated");\n});\n')),(0,a.kt)("h2",{id:"usage-with-asynchronous-code"},"Usage with asynchronous code"),(0,a.kt)("p",null,"Please make sure to use ",(0,a.kt)("inlineCode",{parentName:"p"},"io.to(...).emit(...)")," (or ",(0,a.kt)("inlineCode",{parentName:"p"},"socket.to(...).emit(...)"),") in a synchronous manner."),(0,a.kt)("p",null,"Example with callback:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'// BAD\nconst room = socket.to("room1");\nsaveProduct(() => {\n  room.emit("product-updated");\n});\n\n// GOOD\nsaveProduct(() => {\n  socket.to("room1").emit("product-updated");\n});\n')),(0,a.kt)("p",null,"Example with ",(0,a.kt)("inlineCode",{parentName:"p"},"async/await"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'// BAD\nio.to("room2").emit("details", await fetchDetails());\n\n// GOOD\nconst details = await fetchDetails();\nio.to("room2").emit("details", details);\n')),(0,a.kt)("p",null,"Explanation: the ",(0,a.kt)("inlineCode",{parentName:"p"},"to()")," method does not return a new object, it mutates an attribute on the ",(0,a.kt)("inlineCode",{parentName:"p"},"io")," (respectively, the ",(0,a.kt)("inlineCode",{parentName:"p"}
1,"socket"),") object."),(0,a.kt)("p",null,"This also applies to other broadcast modifiers:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/docs/v3/server-api/#Flag-%E2%80%98local%E2%80%99"},"local")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/docs/v3/server-api/#Flag-%E2%80%98broadcast%E2%80%99"},"broadcast")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"/docs/v3/server-api/#Flag-%E2%80%98volatile%E2%80%99"},"volatile"))),(0,a.kt)("h2",{id:"disconnection"},"Disconnection"),(0,a.kt)("p",null,"Upon disconnection, sockets ",(0,a.kt)("inlineCode",{parentName:"p"},"leave")," all the channels they were part of automatically, and no special teardown is needed on your part."),(0,a.kt)("p",null,"You can fetch the rooms the Socket was in by listening to the ",(0,a.kt)("inlineCode",{parentName:"p"},"disconnecting")," event:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.on("connection", socket => {\n  socket.on("disconnecting", () => {\n    console.log(socket.rooms); // the Set contains at least the socket ID\n  });\n\n  socket.on("disconnect", () => {\n    // socket.rooms.size === 0\n  });\n});\n')),(0,a.kt)("h2",{id:"with-multiple-socketio-servers"},"With multiple Socket.IO servers"),(0,a.kt)("p",null,"Like ",(0,a.kt)("a",{parentName:"p",href:"/docs/v3/broadcasting-events/#With-multiple-Socket-IO-servers"},"global broadcasting"),", broadcasting to rooms also works with multiple Socket.IO servers."),(0,a.kt)("p",null,"You just need to replace the default ",(0,a.kt)("a",{parentName:"p",href:"/docs/v3/glossary/#Adapter"},"Adapter")," by the Redis Adapter. More information about it ",(0,a.kt)("a",{parentName:"p",href:"/docs/v3/using-multiple-nodes/#Passing-events-between-nodes"},"here"),"."),(0,a.kt)(i.Z,{alt:"Broadcasting to all clients in a room with Redis",sources:{light:(0,r.Z)("/images/rooms-redis.png"),dark:(0,r.Z)("/images/rooms-redis-dark.png")},mdxType:"ThemedImage"}),(0,a.kt)("h2",{id:"implementation-details"},"Implementation details"),(0,a.kt)("p",null,'The "room" feature is implemented by what we call an Adapter. This Adapter is a server-side component which is responsible for:'),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"storing the relationships between the Socket instances and the rooms"),(0,a.kt)("li",{parentName:"ul"},"broadcasting events to all (or a subset of) clients")),(0,a.kt)("p",null,"You can find the code of the default in-memory adapter ",(0,a.kt)("a",{parentName:"p",href:"https://github.com/socketio/socket.io-adapter"},"here"),"."),(0,a.kt)("p",null,"Basically, it consists in two ",(0,a.kt)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"},"ES6 Maps"),":"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"sids"),": ",(0,a.kt)("inlineCode",{parentName:"li"},"Map<SocketId, Set<Room>>")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"rooms"),": ",(0,a.kt)("inlineCode",{parentName:"li"},"Map<Room, Set<SocketId>>"))),(0,a.kt)("p",null,"Calling ",(0,a.kt)("inlineCode",{parentName:"p"},'socket.join("the-room")')," will result in:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"in the ",(0,a.kt)("inlineCode",{parentName:"li"},"sids"),' Map, adding "the-room" to the Set identified by the socket ID'),(0,a.kt)("li",{parentName:"ul"},"in the ",(0,a.kt)("inlineCode",{parentName:"li"},"rooms"),' Map, adding the socket ID in the Set identified by the string "the-room"')),(0,a.kt)("p",null,"Those two maps are then used when broadcasting:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"a broadcast to all sockets (",(0,a.kt)("inlineCode",{parentName:"li"},"io.emit()"),") loops through the ",(0,a.kt)("inlineCode",{parentName:"li"},"sids")," Map, and send the packet to all sockets"),(0,a.kt)("li",{parentName:"ul"},"a broadcast to a given room (",(0,a.kt)("inlineCode",{parentName:"li"},'io.to("room21").emit()'),") loops through the Set in the ",(0,a.kt)("inlineCode",{parentName:"li"},"rooms")," Map, and sends the packet to all matching sockets")),(0,a.kt)("p",null,"You can access those objects with:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'// main namespace\nconst rooms = io.of("/").adapter.rooms;\nconst sids = io.of("/").adapter.sids;\n\n// custom namespace\nconst rooms = io.of("/my-namespace").adapter.rooms;\nconst sids = io.of("/my-namespace").adapter.sids;\n')),(0,a.kt)("p",null,"Notes:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"those objects are not meant to be directly modified, you should always use ",(0,a.kt)("a",{parentName:"li",href:"/docs/v3/server-api/#socket-join-room"},(0,a.kt)("inlineCode",{parentName:"a"},"socket.join(...)"))," and ",(0,a.kt)("a",{parentName:"li",href:"/docs/v3/server-api/#socket-leave-room"},(0,a.kt)("inlineCode",{parentName:"a"},"socket.leave(...)"))," instead."),(0,a.kt)("li",{parentName:"ul"},"in a ",(0,a.kt)("a",{parentName:"li",href:"/docs/v3/using-multiple-nodes/"},"multi-server")," setup, the ",(0,a.kt)("inlineCode",{parentName:"li"},"rooms")," and ",(0,a.kt)("inlineCode",{parentName:"li"},"sids"),' objects are not shared between the Socket.IO servers (a room may only "exist" on one server and not on another).')),(0,a.kt)("h2",{id:"room-events"},"Room events"),(0,a.kt)("p",null,"Starting with ",(0,a.kt)("inlineCode",{parentName:"p"},"[email protected]"),", the underlying Adapter will emit the following events:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"create-room")," (argument: room)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"delete-room")," (argument: room)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"join-room")," (argument: room, id)"),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"leave-room")," (argument: room, id)")),(0,a.kt)("p",null,"Example:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-js"},'io.of("/").adapter.on("create-room", (room) => {\n  console.log(`room ${room} was created`);\n});
1\n\nio.of("/").adapter.on("join-room", (room, id) => {\n  console.log(`socket ${id} has joined room ${room}`);\n});\n')))}k.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.