Follow AiTechWorlds on LinkedIn for professional AI content!Follow Now →

Building Real-Time Apps with WebSockets and React

Build real-time web apps with WebSockets and React: live chat, notifications, collaborative features, and Socket.IO integration with step-by-step examples.

A
AiTechWorlds Team
May 27, 2026 6 min read
📱

Get more content like this on Telegram!

Daily AI tips, notes & resources — free

Join Free →

Building Real-Time Apps with WebSockets and React

I built my first "real-time" feature using polling — an API call every 2 seconds to check for new messages. It worked, technically. It also hammered the server with thousands of unnecessary requests per hour.

WebSockets solved that problem and unlocked a category of features that polling simply can't provide elegantly: seeing someone else's cursor move, watching a document update as a teammate types, getting an instant notification when a payment goes through.

This tutorial builds a real-time chat application using WebSockets and React. By the end, you'll understand the full stack: native WebSockets, the React integration pattern, and Socket.IO for production-grade real-time.


Understanding WebSockets

How They Work

HTTP request:
Client → Request → Server → Response → Connection closes

WebSocket:
Client → Upgrade request → Server → Connection stays open
Client ←→ Server (messages can flow both directions, anytime)

The WebSocket handshake starts as an HTTP request with an Upgrade: websocket header. Once the server accepts, the connection upgrades to the WebSocket protocol and stays open.

Native WebSocket API

// Browser-side
const ws = new WebSocket('ws://localhost:3001');

ws.onopen = () => {
  console.log('Connected');
  ws.send(JSON.stringify({ type: 'greeting', text: 'Hello server!' }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

ws.onclose = () => console.log('Disconnected');
ws.onerror = (error) => console.error('Error:', error);

// Send a message
ws.send(JSON.stringify({ type: 'chat', text: 'Hello everyone!' }));

Part 1: Simple WebSocket Server

npm init -y
npm install ws
// server.js
const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 3001 });

const clients = new Set();

wss.on('connection', (ws) => {
  clients.add(ws);
  console.log(`Client connected. Total: ${clients.size}`);
  
  // Notify all clients about new connection
  broadcast({ type: 'system', text: 'A user joined the chat' });
  
  ws.on('message', (rawData) => {
    try {
      const message = JSON.parse(rawData);
      console.log('Received:', message);
      
      // Broadcast to all connected clients
      broadcast(message);
    } catch (err) {
      console.error('Invalid message format:', err);
    }
  });
  
  ws.on('close', () => {
    clients.delete(ws);
    broadcast({ type: 'system', text: 'A user left the chat' });
  });
});

function broadcast(data) {
  const message = JSON.stringify(data);
  clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(message);
    }
  });
}

console.log('WebSocket server running on ws://localhost:3001');

Part 2: React Chat Component

Custom Hook for WebSocket

// hooks/useWebSocket.ts
import { useState, useEffect, useRef, useCallback } from 'react';

interface Message {
  id: number;
  type: 'chat' | 'system';
  text: string;
  username?: string;
  timestamp: string;
}

function useWebSocket(url: string, username: string) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [connected, setConnected] = useState(false);
  const wsRef = useRef<WebSocket | null>(null);
  
  useEffect(() => {
    const ws = new WebSocket(url);
    wsRef.current = ws;
    
    ws.onopen = () => {
      setConnected(true);
      // Announce ourselves
      ws.send(JSON.stringify({
        type: 'join',
        username,
        timestamp: new Date().toISOString(),
      }));
    };
    
    ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      setMessages(prev => [...prev, { ...message, id: Date.now() }]);
    };
    
    ws.onclose = () => setConnected(false);
    ws.onerror = () => setConnected(false);
    
    return () => {
      ws.close();
    };
  }, [url, username]);
  
  const sendMessage = useCallback((text: string) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({
        type: 'chat',
        text,
        username,
        timestamp: new Date().toISOString(),
      }));
    }
  }, [username]);
  
  return { messages, connected, sendMessage };
}

export default useWebSocket;

Chat UI Component

// components/Chat.tsx
import { useState, useRef, useEffect } from 'react';
import useWebSocket from '../hooks/useWebSocket';

function Chat({ username }: { username: string }) {
  const [input, setInput] = useState('');
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const { messages, connected, sendMessage } = useWebSocket('ws://localhost:3001', username);
  
  // Auto-scroll to latest message
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);
  
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim()) return;
    sendMessage(input.trim());
    setInput('');
  };
  
  return (
    <div className="flex flex-col h-screen max-w-lg mx-auto">
      {/* Header */}
      <div className="p-4 bg-blue-600 text-white flex items-center justify-between">
        <h1 className="font-bold">Chat Room</h1>
        <span className={`text-xs px-2 py-1 rounded-full ${
          connected ? 'bg-green-500' : 'bg-red-500'
        }`}>
          {connected ? 'Connected' : 'Disconnected'}
        </span>
      </div>
      
      {/* Messages */}
      <div className="flex-1 overflow-y-auto p-4 space-y-2">
        {messages.map(msg => (
          <div key={msg.id} className={`
            ${msg.type === 'system' ? 'text-center text-gray-400 text-sm' : ''}
            ${msg.type === 'chat' && msg.username === username ? 'text-right' : ''}
          `}>
            {msg.type === 'chat' && (
              <div className={`inline-block max-w-[70%] rounded-2xl px-4 py-2 ${
                msg.username === username 
                  ? 'bg-blue-600 text-white' 
                  : 'bg-gray-100 text-gray-900'
              }`}>
                {msg.username !== username && (
                  <p className="text-xs text-gray-500 mb-1">{msg.username}</p>
                )}
                <p>{msg.text}</p>
              </div>
            )}
            {msg.type === 'system' && <p>{msg.text}</p>}
          </div>
        ))}
        <div ref={messagesEndRef} />
      </div>
      
      {/* Input */}
      <form onSubmit={handleSubmit} className="p-4 border-t flex gap-2">
        <input
          type="text"
          value={input}
          onChange={e => setInput(e.target.value)}
          placeholder="Type a message..."
          className="flex-1 border rounded-full px-4 py-2 focus:outline-none"
          disabled={!connected}
        />
        <button
          type="submit"
          disabled={!connected || !input.trim()}
          className="bg-blue-600 text-white rounded-full px-6 py-2 disabled:opacity-50"
        >
          Send
        </button>
      </form>
    </div>
  );
}

Part 3: Production with Socket.IO

For production apps, Socket.IO adds automatic reconnection, rooms, and a better API:

npm install socket.io
npm install socket.io-client

Server with Socket.IO

// server.js
const { Server } = require('socket.io');
const httpServer = require('http').createServer();

const io = new Server(httpServer, {
  cors: { origin: 'http://localhost:5173', methods: ['GET', 'POST'] }
});

io.on('connection', (socket) => {
  console.log(`User connected: ${socket.id}`);
  
  socket.on('join-room', (roomId, username) => {
    socket.join(roomId);
    socket.to(roomId).emit('user-joined', { username });
  });
  
  socket.on('chat-message', (roomId, message) => {
    // Broadcast to everyone in room except sender
    socket.to(roomId).emit('chat-message', message);
  });
  
  socket.on('disconnect', () => {
    console.log(`User disconnected: ${socket.id}`);
  });
});

httpServer.listen(3001);

React with Socket.IO Client

import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';

function useSocket(serverUrl: string) {
  const socketRef = useRef<Socket | null>(null);
  
  useEffect(() => {
    socketRef.current = io(serverUrl, {
      reconnection: true,
      reconnectionAttempts: 5,
      reconnectionDelay: 1000,
    });
    
    return () => {
      socketRef.current?.disconnect();
    };
  }, [serverUrl]);
  
  return socketRef;
}

WebSockets vs SSE vs Polling

PollingSSEWebSocket
DirectionClient → Server onlyServer → Client onlyBidirectional
HTTP overheadHighLowLow
ReconnectionManualAutomaticLibrary-dependent
Use caseSimple, infrequent updatesLive feeds, notificationsChat, collaboration
ComplexityLowLowMedium

For the broader React patterns that complement real-time features, our React hooks tutorial covers the custom hooks pattern used here. For state management when WebSocket messages update global state, see our React state management 2025 guide. And for the Node.js backend serving WebSocket connections, our Node.js and Express REST API guide covers the server setup.


Frequently Asked Questions

What is a WebSocket vs HTTP?

HTTP is request-response (connection closes after each exchange). WebSocket is a persistent bidirectional connection — both sides can send messages anytime.

WebSockets or Server-Sent Events?

SSE for one-way server → client (notifications, feeds). WebSockets for bidirectional (chat, collaboration, games).

What is Socket.IO?

A library wrapping WebSockets with rooms, reconnection, and an event-based API. Better than raw WebSockets for Node.js server apps.

How do I use WebSockets in React?

A custom hook: create connection in useEffect, clean up (close) in the return function, store messages in state.

Share this article:

Frequently Asked Questions

HTTP is a request-response protocol: the client sends a request, the server sends a response, and the connection closes. WebSocket is a persistent, bidirectional connection: client and server can send messages to each other at any time without a new request. This makes WebSockets ideal for real-time features: live chat, notifications, collaborative editing, live dashboards, and multiplayer games.
A

AiTechWorlds Team

✓ Verified Writer

The AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.

Related Articles

10K+ Members Growing Daily

Get Free AI Notes Daily

Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!

📚 Free Study Notes🤖 AI Tips Daily⚡ Prompt Templates💻 Coding Resources
Join Free Channel

No spam. Leave anytime.

!