Socket.io with React and Nodejs | Websocket API

socket.io tutorial

Implementing Socket.io in a React + Node.js Chat App

A step-by-step beginner's guide to real-time communication — from zero to live messaging.

DEV
Developer Blog
~10 min read

What is Socket.io and Why Do We Need It?

Normally in your app, communication works like a question-and-answer game. The frontend asks, the backend answers, and then the conversation is over.

Normal HTTP Request/Response
Frontend asks:
"Give me messages"
→
Backend replies
→
Connection closes

The problem? The frontend has to keep asking over and over to know if new messages arrived. This is slow and wasteful.

Socket.io solves this by creating a permanent open connection — messages flow both ways, instantly, in real time.

Socket.io WebSocket Connection
Frontend
⟵
Live Connection
(always open)
⟶
Backend

Step 1 — Install the Packages

You need two different packages — one for each side of your app.

terminal · backend folder
# Run this inside your backend folder
npm install socket.io
terminal · frontend folder
# Run this inside your React frontend folder
npm install socket.io-client
Note: Backend uses socket.io and frontend uses socket.io-client. They are two different packages that are built to work together.

Step 2 — Setup Socket.io in the Backend

Your server.js currently uses app.listen() from Express. We need to swap that out for a raw HTTP server, because Socket.io needs to attach directly to it.

server.js · backend
const express = require('express');
const http    = require('http');         // Node's built-in http module
const { Server } = require('socket.io'); // import socket.io
const cors   = require('cors');

const app    = express();

// Wrap express inside an http server
const server = http.createServer(app);

// Attach socket.io to the http server
const io = new Server(server, {
  cors: { origin: "http://localhost:3000", methods: ["GET", "POST"] }
});

app.use(cors());
app.use(express.json());

// Your existing REST routes stay here ↓

// Socket.io logic starts here
io.on('connection', (socket) => {
  console.log('A user connected:', socket.id);

  // Listen for a message from frontend
  socket.on('send_message', (data) => {
    // Broadcast it to ALL connected users
    io.emit('receive_message', data);
  });

  socket.on('disconnect', () => {
    console.log('User disconnected:', socket.id);
  });
});

// IMPORTANT: Use server.listen, NOT app.listen
server.listen(5000, () => {
  console.log('Server running on port 5000');
});

What each key part does:

CodeWhat it does
http.createServer(app)Wraps Express inside a basic Node HTTP server
new Server(server, {...})Attaches socket.io to that server
io.on('connection', ...)Runs every time a new user connects
socket.idA unique ID auto-assigned to every connected user
socket.on('send_message', ...)Listens for an event called send_message from frontend
io.emit('receive_message', ...)Sends receive_message to everyone who is connected

Step 3 — Create a Socket File in React

Create a new file src/socket.js. This keeps your socket connection in one place so the same connection is reused across your whole app.

src/socket.js · frontend
import { io } from 'socket.io-client';

// Connect to your backend
const socket = io('http://localhost:5000');

export default socket;
Why a separate file? If you create the socket connection inside a component, a brand new connection is made every time that component re-renders. That's wasteful. A separate file ensures you reuse one single connection.

Step 4 — Use Socket.io in Your Chat Component

src/components/Chat.jsx · frontend
import { useState, useEffect } from 'react';
import socket from '../socket'; // import our socket

function Chat() {
  const [message, setMessage]       = useState('');
  const [allMessages, setAllMessages] = useState([]);

  useEffect(() => {
    // LISTEN for incoming messages from backend
    socket.on('receive_message', (data) => {
      setAllMessages((prev) => [...prev, data]);
    });

    // Cleanup: stop listening when component unmounts
    return () => { socket.off('receive_message'); };
  }, []); // [] = run only once on load

  const sendMessage = () => {
    if (message.trim() === '') return;

    const messageData = {
      text: message,
      time: new Date().toLocaleTimeString(),
    };

    // SEND message to backend
    socket.emit('send_message', messageData);
    setMessage('');
  };

  return (
    <div>
      <div style={{ height: '300px', overflowY: 'scroll' }}>
        {allMessages.map((msg, i) => (
          <div key={i}>{msg.time}: {msg.text}</div>
        ))}
      </div>
      <input
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
        placeholder="Type a message..."
      />
      <button onClick={sendMessage}>Send</button>
    </div>
  );
}

export default Chat;

How the Full Flow Works

Here is what happens from the moment a user types a message to when everyone sees it:

End-to-end message flow
User types "Hello"
& clicks Send
→
socket.emit
'send_message'
→
Backend receives
socket.on
→
io.emit
'receive_message'
→
All users see
the message ✓

Quick Setup Checklist

  • npm install socket.io in the backend folder
  • npm install socket.io-client in the frontend folder
  • Changed app.listen to server.listen in backend
  • Created src/socket.js in React
  • Used socket.emit() to send events
  • Used socket.on() to receive events

What to Learn Next

  • Rooms — for private chats between specific users
  • Sending username with each message
  • Showing "User is typing..." indicators
  • Displaying who is currently online
  • Persisting messages to a database (MongoDB)

Post a Comment

Previous Post Next Post