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)

Comments