How to Manage Users in Socket.io in Node.js ? (original) (raw)
Last Updated : 3 Jun, 2026
In a real-time application, the server needs a way to keep track of who is connected and how to communicate with them individually or in groups. Socket.io handles this by assigning each client a unique connection and allowing the server to manage these connections efficiently. User management in Socket.io typically involves the following steps:
- **Map users to socket IDs: Link
socket.idwith user data (e.g., username). - **Track active users: Store connected users in an object or map.
- **Remove on disconnect: Delete user data when the
disconnectevent fires. - **Send targeted messages: Use socket IDs to emit events to specific users.
- **Group with rooms: Use rooms to manage communication for multiple users.
**Steps to Create Application
**Backend Setup
**Step 1: Initialize the project using the command
npm init
**Step 2 : Install the requires dependencies:
npm i cors express nodemon socket.io http
**Project Structure for Backend:
The updated dependencies in **package.json file of backend will look like:
{
"dependencies": {
"cors": "^2.8.5",
"express": "^4.18.2",
"http": "^0.0.1-security",
"nodemon": "^3.0.1",
"socket.io": "^4.7.2"
}
}
**Example: Socket.on listens for a join event emitted by the frontend; the backend then emits a message event to notify that the user joined. It also handles connection, messaging, and user management functions like add, remove, and get users.
Node ``
// Filename - index.js
const express = require('express'); const socketio = require('socket.io'); const http = require('http'); const cors = require('cors'); const { addUser, removeUser, getUser, getUsersInRoom } = require("./users");
const app = express(); const server = http.createServer(app); const io = socketio(server); app.use(cors())
io.on("connection", (socket) => { socket.on('join', ({ name, room }, callback) => {
const { error, user } = addUser(
{ id: socket.id, name, room });
if (error) return callback(error);
// Emit will send message to the user
// who had joined
socket.emit('message', {
user: 'admin', text:
`${user.name},
welcome to room ${user.room}.`
});
// Broadcast will send message to everyone
// in the room except the joined user
socket.broadcast.to(user.room)
.emit('message', {
user: "admin",
text: `${user.name}, has joined`
});
socket.join(user.room);
io.to(user.room).emit('roomData', {
room: user.room,
users: getUsersInRoom(user.room)
});
callback();
})
socket.on('sendMessage', (message, callback) => {
const user = getUser(socket.id);
io.to(user.room).emit('message',
{ user: user.name, text: message });
io.to(user.room).emit('roomData', {
room: user.room,
users: getUsersInRoom(user.room)
});
callback();
})
socket.on('disconnect', () => {
const user = removeUser(socket.id);
if (user) {
io.to(user.room).emit('message',
{
user: 'admin', text:
`${user.name} had left`
});
}
})})
server.listen(process.env.PORT || 5000,
() => console.log(Server has started.));
`` Node `
// Filename - User.js
const users = [];
const addUser = ({ id, name, room }) => { name = name.trim().toLowerCase(); room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return { user };}
const removeUser = (id) => { const index = users.findIndex((user) => { user.id === id });
if (index !== -1) {
return users.splice(index, 1)[0];
}}
const getUser = (id) => users .find((user) => user.id === id);
const getUsersInRoom = (room) => users .filter((user) => user.room === room);
module.exports = { addUser, removeUser, getUser, getUsersInRoom };
`
**Frontend Setup:
**Step 1: Install react for frontend using this command in terminal
npx create react-app client
**Step 2: After react installed, install dependencies for Project inside client folder.
cd client
npm i query-string react-emoji react-router socket.io-client
**Project Structure for frontend:
The updated dependencies in **package.json file of frontend will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-emoji": "^0.5.0",
"react-router": "^6.17.0",
"react-scripts": "5.0.1",
"socket.io-client": "^4.7.2",
"web-vitals": "^2.1.4"
}, **Step 3: Inside App.js, create routes for the pages join page and chat page and import the components for both pages to display on that route.
**Filename: App.js
CSS `
/* Filename - Chat/Chat.css */
.outerContainer { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #1A1A1D; }
.container { display: flex; flex-direction: column; justify-content: space-between; background: #FFFFFF; border-radius: 8px; height: 60%; width: 35%; }
@media (min-width: 320px) and (max-width: 480px) { .outerContainer { height: 100%; }
.container {
width: 100%;
height: 100%;
}}
@media (min-width: 480px) and (max-width: 1200px) { .container { width: 60%; } }
CSS
/* Filename - InfoBar/InfoBar.css*/
.infoBar { display: flex; align-items: center; justify-content: space-between; background: #2979FF; border-radius: 4px 4px 0 0; height: 60px; width: 100%; }
.leftInnerContainer { flex: 0.5; display: flex; align-items: center; margin-left: 5%; color: white; }
.rightInnerContainer { display: flex; flex: 0.5; justify-content: flex-end; margin-right: 5%; }
.onlineIcon { margin-right: 5%; }
CSS
/* Filename - Input/Input.css */
.form { display: flex; border-top: 2px solid #D3D3D3; }
.input { border: none; border-radius: 0; padding: 5%; width: 80%; font-size: 1.2em; }
input:focus, textarea:focus, select:focus { outline: none; }
.sendButton { color: #fff !important; text-transform: uppercase; text-decoration: none; background: #2979FF; padding: 20px; display: inline-block; border: none; width: 20%; }
CSS
/* Filename - Join/Join.css*/
html, body { font-family: 'Roboto', sans-serif; padding: 0; margin: 0; }
#root { height: 100vh; }
- { box-sizing: border-box; }
.joinOuterContainer { display: flex; justify-content: center; text-align: center; height: 100vh; align-items: center; background-color: #1A1A1D; }
.joinInnerContainer { width: 20%; }
.joinInput { border-radius: 0; padding: 15px 20px; width: 100%; }
.heading { color: white; font-size: 2.5em; padding-bottom: 10px; border-bottom: 2px solid white; }
.button { color: #fff !important; text-transform: uppercase; text-decoration: none; background: #2979FF; padding: 20px; border-radius: 5px; display: inline-block; border: none; width: 100%; }
.mt-20 { margin-top: 20px; }
@media (min-width: 320px) and (max-width: 480px) { .joinOuterContainer { height: 100%; }
.joinInnerContainer {
width: 90%;
}}
button:focus { outline: 0; }
CSS
/* Filename - Messages/Message/Message.css */
.messageBox { background: #F3F3F3; border-radius: 20px; padding: 5px 20px; color: white; display: inline-block; max-width: 80%; }
.messageText { width: 100%; letter-spacing: 0; float: left; font-size: 1.1em; word-wrap: break-word; }
.messageText img { vertical-align: middle; }
.messageContainer { display: flex; justify-content: flex-end; padding: 0 5%; margin-top: 3px; }
.sentText { display: flex; align-items: center; font-family: Helvetica; color: #828282; letter-spacing: 0.3px; }
.pl-10 { padding-left: 10px; }
.pr-10 { padding-right: 10px; }
.justifyStart { justify-content: flex-start; }
.justifyEnd { justify-content: flex-end; }
.colorWhite { color: white; }
.colorDark { color: #353535; }
.backgroundBlue { background: #2979FF; }
.backgroundLight { background: #F3F3F3; }
CSS
/* Filename Messages/Messages.css */
.messages { padding: 5% 0; overflow: auto; flex: auto; }
CSS
/* Filename - TextContainer/TextContainer.css */
.textContainer { display: flex; flex-direction: column; margin-left: 100px; color: rgb(201, 25, 25); height: 60%; justify-content: space-between; }
.activeContainer { display: flex; align-items: center; margin-bottom: 50%; }
.activeItem { display: flex; align-items: center; }
.activeContainer img { padding-left: 10px; }
.textContainer h1 { margin-bottom: 0px; }
@media (min-width: 320px) and (max-width: 1200px) { .textContainer { display: none; } }
JavaScript
// Filename - App.js import React from 'react';
import Chat from './components/Chat/Chat'; import Join from './components/Join/Join';
import { BrowserRouter as Router, Route } from "react-router-dom";
const App = () => { return ( ); }
export default App;
JavaScript
// Filename - Chat/Chat.js
import React, { useState, useEffect } from "react"; import queryString from "query-string"; import io from 'socket.io-client'; import TextContainer from '../TextContainer/TextContainer'; import Messages from '../Messages/Messages'; import InfoBar from '../InfoBar/InfoBar'; import Input from '../Input/Input';
import "./Chat.css";
let connectionOptions = { "force new connection": true, "reconnectionAttempts": "Infinity", "timeout": 10000, "transports": ["websocket"] };
let socket = io.connect('https://localhost:5000', connectionOptions);
const Chat = ({ location }) => {
const [name, setName] = useState('');
const [room, setRoom] = useState("");
const [users, setUsers] = useState('');
const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const ENDPOINT = 'localhost:5000';
useEffect(() => {
const { name, room } = queryString.parse(location.search);
setName(name);
setRoom(room);
socket.emit('join', { name, room }, (error) => {
if (error) {
alert(error);
}
})
return () => {
socket.emit('disconnect');
socket.off();
}
}, [ENDPOINT, location.search]);
useEffect(() => {
socket.on('message', (message) => {
setMessages([...messages, message]);
})
socket.on("roomData", ({ users }) => {
setUsers(users);
});
}, [messages, users])
//Function for Sending Message
const sendMessage = (e) => {
e.preventDefault();
if (message) {
socket.emit('sendMessage', message, () => setMessage(''))
}
}
console.log(message, messages);
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Messages messages={messages} name={name} />
<Input message={message} setMessage={setMessage}
sendMessage={sendMessage} />
</div>
<TextContainer users={users} />
</div>
)};
export default Chat;
JavaScript
// Filename - InfoBar/InfoBar.js
import React from 'react';
import './InfoBar.css';
const InfoBar = ({ room }) => (
{room}
export default InfoBar;
JavaScript
// Filename - Input/Input.js
import React from 'react';
import './Input.css';
const Input = ({ setMessage, sendMessage, message }) => (
<input className="input" type="text" placeholder="Type a message..." value={message} onChange={({ target: { value } }) => setMessage(value)} onKeyPress={event => event.key === 'Enter' ? sendMessage(event) : null} /> <button className="sendButton" onClick={e => sendMessage(e)}>Send )export default Input;
` JavaScript ``
// Filename - Join/Join.js
import React, { useState } from "react"; import { Link } from 'react-router-dom';
import './Join.css';
const Join = () => {
const [name, setName] = useState('');
const [room, setRoom] = useState("");
return (
<div className="joinOuterContainer">
<div className="joinInnerContainer">
<h1 className="heading">Join</h1>
<div>
<input placeholder="Name"
className="joinInput"
type="text"
onChange=
{(event) => setName(event.target.value)} />
</div>
<div>
<input placeholder="Room"
className="joinInput mt-20"
type="text" onChange=
{(event) => setRoom(event.target.value)} />
</div>
<Link onClick={e => (!name || !room) ?
e.preventDefault() : null}
to={`/chat?name=${name}&room=${room}`
}>
<button className={'button mt-20'}
type="submit">Sign In
</button>
</Link>
</div>
</div>
);};
export default Join;
`` JavaScript `
// Filename - Messages/Message/Message.js
import React from 'react';
import './Message.css';
import ReactEmoji from 'react-emoji';
const Message = ({ message: { text, user }, name }) => { let isSentByCurrentUser = false;
const trimmedName = name.trim().toLowerCase();
if (user === trimmedName) {
isSentByCurrentUser = true;
}
return (
isSentByCurrentUser
? (
<div className="messageContainer justifyEnd">
<p className="sentText pr-10">{trimmedName}</p>
<div className="messageBox backgroundBlue">
<p className="messageText colorWhite">
{ReactEmoji.emojify(text)}
</p>
</div>
</div>
)
: (
<div className="messageContainer justifyStart">
<div className="messageBox backgroundLight">
<p className="messageText colorDark">
{ReactEmoji.emojify(text)}
</p>
</div>
<p className="sentText pl-10 ">{user}</p>
</div>
)
);}
export default Message;
JavaScript
// Filename - Messages/Messages.js
import React from 'react';
import Message from './Message/Message';
import './Messages.css';
const Messages = ({ messages, name }) => (
</div>);
export default Messages;
JavaScript
// Filename TextContainer/TextContainer.js
import React from "react";
import onlineIcon from "../../icons/onlineIcon.png";
import "./TextContainer.css";
const TextContainer = ({ users }) => (
People currently chatting:
{users.map(({ name }) => (
{name}
))}
export default TextContainer;
`
**Output: Open the browser and type localhost 3000 to see the application running.