Skip to main content

Command Palette

Search for a command to run...

Socket.io in MERN

Updated
7 min read
Socket.io in MERN

Building basic socket.io project

There will be total two sections
1. Server side socket.io
2. Client side socket.io

WebSocket vs Socket.io

WebSocket is the raw, low-level protocol that allows us to establish a direct connection between the client and server by writing code that manually handles the socket connection.

On the other hand, Socket.IO is a library built on top of WebSocket (and other protocols) to simplify
real-time communication. It removes much of the boilerplate, provides fallbacks when WebSocket isn’t supported, and instead of directly creating a socket connection, it upgrades the existing HTTP connection to a persistent WebSocket-like connection.

1. Server side

  • In server side initially import the socket.io
            npm install socket.io
  • After installing socket.io write the following code where we initialize the basic Http server and then upgrade connection to make a Socket connection

  • Server from socket.io → this is the main class that lets us create a Socket.IO server instance and handle real-time communication.

      import express from 'express';
      import http from 'http';
      import { Server } from 'socket.io';
      import cors from 'cors';
    
  • Create :

    1. const app creates an Express instance that will handle routes and middleware.

    2. const server uses Node’s ‘http.createServer()’ to wrap the Express app so that it can handle both HTTP requests and be upgraded later for WebSocket connections.

    3. const io creates a new Socket.IO server instance, which upgrades the HTTP server to handle real-time communication.

    const app = express();
    // http request cors policy frontend
    app.use(cors({
        origin : "http://localhost:5173",
        credentials : true
    }));
    const server = http.createServer(app);
    // Create server and make a cors policy to interact with frontend
    const io = new Server(server, {
        cors : {
            origin : "http://localhost:5173"
        }
    });
    
  • Connection : In Socket.IO, the first step is to listen for when a client connects. This is done using the "connection" event.

    1. The "connection" event fires whenever a new client connects.

    2. The socket object represents that specific client’s connection.

    3. Using this socket, we can now listen to events (socket.on) or send events (socket.emit) to that particular client.

          io.on("connection", (socket) => {
              console.log("A client connected:", socket.id);
          });
  • Events : Socket.IO works on an event-driven model, very similar to Node.js’s built-in EventEmitter.

    • .on(event,callback) → listens for an event.

    • .emit(event,data) → emits (sends) an event.

    This pattern makes it easy to build real-time communication, since both the server and the client can send and listen for events.

          io.on('connection',(socket) => {
              // console.log(`User connection with id ${socket.id}`)
              socket.on('message',(msg) => {
                  console.log(`User \({socket.id} sent message : \){msg}`);
                  socket.broadcast.emit('message',{
                      text : msg,
                      user : socket.id,
                      timestamp : new Date()
                  });
              });
          });
  • After particular client is connected with the connection we check the event ‘message’ for that client only and if it hits the events then event emits/sends the data requested from client to all other socket connections.
    Here are two possibilities :

    1. socket.emit () → emits data only to sender

    2. socket.broadcast.emit() → emits data to all others clients excluding itself (sender)

    3. io.emit() → emits data to all connected clients , including itself (sender)

  • Finally, You can start a server by listening to a port
    Since we wrapped our Express app with http.createServer(), this single server can now handle both HTTP requests and Socket.IO connections.

    server.listen(port, () => {
      console.log(`🚀 Server is running at http://localhost:${port}`);
    });
    

2. Client side

  • In server side initially import the socket.io-client
    We would be using in React.js so create React folder beforehand

            npm install socket.io-client
    
  • Create : We need some states to handle the data , create the socket instance.

    1. sentMessage starts as an empty string "" because it represents text input; initializing with null would cause issues when binding to an input field.

    2. socket is initialized as null and later set to the Socket.IO client instance once the connection is created.

    3. rMessages begins as an empty array [] since it will hold a list of received messages that we render in the UI.

            const [sentMessage, setSentMessage] = useState("");
            const [socket, setSocket] = useState(null);
            const [rMessages, setRMessages] = useState([]);
      
  • Connection : Establish a connection with Socket.IO inside useEffect()

    1. This ensures the connection is created when the component mounts.

    2. We also clean it up when the component unmounts.

            useEffect(() => {
                const newSocket = io("http://localhost:8000");
            
                newSocket.on("connect", () => {
                  console.log(`Client with ${newSocket.id} connected our server`);
                });
            
                setSocket(newSocket);
            
            }, []);
      
  • Events : Manage the socket events and handle the data

    1. We listen for the "message" event from the server using newSocket.on.

    2. Every incoming message is wrapped with an extra field type : 'received' so we can style or identify it differently in the UI.

    3. Finally, we update the rMessages state by appending the new message to the existing list.

            newSocket.on("message", (msg) => {
                  const receivedMsg = {
                    ...msg,
                    type : 'received'
                  }
                  console.log('Received msg',receivedMsg);
                  setRMessages((prev) => [...prev, receivedMsg]);
            });
      
  • Cleanup : useEffect() has a cleanup function

    1. It runs when the component unmounts or re-renders.

    2. Calling newSocket.disconnect() ensures we close the socket connection properly, avoiding duplicate connections and memory leaks.

    3. This keeps the app efficient and prevents multiple event listeners from stacking up.

    Another important thing you can notice is , when you write console.log(socket.id) for a ‘connect’ inside newSocket lile (newSocket.io) you can get a connection id in console twice because React 18 is set in
    Strict mode due to which it intentionally runs useEffect() twice to help catch bugs
    But practically in production mode it is only executed once.

      return () => {
        newSocket.disconnect();
        console.log("Socket disconnected");
      };
  • Data Handling : Depending upon what type of data you are sending extract or de-structure your data.
    Here we would be using simple .map function to render Messages
  const renderMessages = () => {
    return rMessages.map((msg, index) => <div key={index}>
      <p className={msg.type === 'sent' ? 'bg-red-100' : 'bg-blue-100'}>{msg.text}</p>
    </div>);
  };

Additional info : If you want to know how we can send / emit to particular room or something similar then you need to create a ‘.to(roomId)’

  • socket.on("joinRoom", roomId) → Listen for a client’s request to join a room.

  • socket.join(roomId) → Add that client’s socket to the room.

  • socket.to(roomId).emit("message", data) → Send a message to all clients in the room except the sender.

    If you want to include the sender as well, use io.to(roomId).emit("message", data).

  • socket.leave(roomId) → Remove the client’s socket from the room.

io.on("connection", (socket) => {
  console.log("User connected:", socket.id);

  socket.on("joinRoom", (roomId) => {
    socket.join(roomId);
    console.log(`\({socket.id} joined room \){roomId}`);
  });

  socket.on("message", (data) => {
    socket.to(data.roomId).emit("message", data.text);
  });

  socket.on("leaveRoom", (roomId) => {
    socket.leave(roomId);
    console.log(`\({socket.id} left room \){roomId}`);
  });
});

Below is the link where in you can go through a very simple chat-app as well as little bit polished a better UI chat-app.

Twitter : https://x.com/r8hitpatil

Github : https://github.com/r8hitpatil

LinkedIn : https://www.linkedin.com/in/r8hitpatil

💡
You can always learn from the best available on Yt and I’m no professional ( still a student learning new stuff ) So pls do check-out Socket.io and go through their docs or else learn from gpt anyways