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:

**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; }

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

Leave
);

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 }) => (

{messages.map((message, i) =>
)}

</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 }) => (

{users ? (

People currently chatting:

{users.map(({ name }) => (
{name} Online Icon
))}

) : null}
);

export default TextContainer;

`

**Output: Open the browser and type localhost 3000 to see the application running.