Building Real-Time Applications with WebSockets
Learn how to build real-time chat applications using WebSockets, Socket.IO, and Next.js. From basic concepts to advanced implementation patterns, discover how to create responsive, real-time user experiences.
Complete Tutorial Code
Follow along with the complete source code for this WebSocket tutorial. Includes a full real-time chat application with Next.js and Socket.IO.
View on GitHubIntroduction
WebSockets have revolutionized how we build real-time applications on the web. Unlike traditional HTTP requests that follow a request-response pattern, WebSockets enable bidirectional communication between clients and servers, making them perfect for chat applications, live updates, collaborative editing, and real-time gaming.
This comprehensive tutorial will guide you through building a complete real-time chat application from scratch. We'll explore WebSocket fundamentals, implement a Socket.IO server, and create a responsive React interface that demonstrates real-time messaging capabilities.
What are WebSockets?
WebSockets provide a persistent, full-duplex communication channel between a client and server. Once established, both the client and server can send data to each other at any time, enabling real-time communication without the overhead of traditional HTTP polling.
Traditional HTTP
Client → Server (Request)Server → Client (Response)Connection ClosedRequest-response cycle with connection overhead
WebSocket
Client ↔ Server
Persistent Connection
Real-time Data Flow
Bidirectional CommunicationPersistent connection for real-time communication
Project Overview
Our WebSocket tutorial demonstrates a real-time chat application that showcases the power of bidirectional communication. The application features instant messaging, connection status indicators, and a clean, responsive interface built with modern web technologies.
Key Features
Tech Stack
Our WebSocket application uses a modern tech stack that provides excellent developer experience and production-ready performance:
Project Structure
The application follows a clean, modular architecture with clear separation of concerns:
websocket-tutorial/
├── src/
│ ├── app/
│ │ ├── page.tsx # Main chat interface
│ │ ├── layout.tsx # App layout
│ │ └── globals.css # Global styles
│ ├── hooks/
│ │ └── useSocket.ts # Custom hook for Socket.IO connection
│ └── types/
│ └── message.ts # Message type definitions
├── server.ts # Socket.IO server with Next.js integration
├── package.json
└── README.mdHow It Works
Server Side Implementation
The server integrates Socket.IO with Next.js to handle both traditional HTTP requests and WebSocket connections. It creates an HTTP server that listens for chat message events from clients and broadcasts received messages to all connected clients.
// server.ts
import { createServer } from 'http';
import { Server } from 'socket.io';
import next from 'next';
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handler = app.getRequestHandler();
app.prepare().then(() => {
const server = createServer(handler);
const io = new Server(server);
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
socket.on('chat message', (msg) => {
// Broadcast message to all connected clients
io.emit('chat message', msg);
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
server.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
});Client Side Implementation
The client side uses a custom React hook to manage the WebSocket connection, track connection status, handle incoming messages, and provide a clean API for sending messages.
Custom Socket Hook
// src/hooks/useSocket.ts
import { useEffect, useState } from 'react';
import { io, Socket } from 'socket.io-client';
import { Message } from '@/types/message';
export const useSocket = () => {
const [socket, setSocket] = useState<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
const socketInstance = io();
socketInstance.on('connect', () => {
setIsConnected(true);
});
socketInstance.on('disconnect', () => {
setIsConnected(false);
});
socketInstance.on('chat message', (message: Message) => {
setMessages((prev) => [...prev, message]);
});
setSocket(socketInstance);
return () => socketInstance.close();
}, []);
const sendMessage = (text: string) => {
if (socket && text.trim()) {
const message: Message = {
id: Date.now().toString(),
text,
timestamp: new Date(),
isSent: true,
};
socket.emit('chat message', message);
}
};
return { socket, isConnected, messages, sendMessage };
};Message Type Definition
// src/types/message.ts
export interface Message {
id: string;
text: string;
timestamp: Date | string;
isSent: boolean;
}Main Chat Interface
The main chat component uses the custom hook to provide a complete chat interface with message display, input handling, and connection status indicators.
// src/app/page.tsx
'use client';
import { useState, useRef, useEffect } from 'react';
import { useSocket } from '@/hooks/useSocket';
export default function ChatPage() {
const { isConnected, messages, sendMessage } = useSocket();
const [inputValue, setInputValue] = useState('');
const messagesEndRef = useRef<HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (inputValue.trim()) {
sendMessage(inputValue);
setInputValue('');
}
};
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<div className="flex items-center gap-2 mb-4">
<div className={`w-3 h-3 rounded-full ${
isConnected ? 'bg-green-500' : 'bg-red-500'
}`} />
<span className="text-sm text-gray-600">
{isConnected ? 'Connected' : 'Disconnected'}
</span>
</div>
<div className="flex-1 overflow-y-auto mb-4 space-y-2">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${
message.isSent ? 'justify-end' : 'justify-start'
}`}
>
<div
className={`max-w-xs px-4 py-2 rounded-lg ${
message.isSent
? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-800'
}`}
>
{message.text}
</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="flex gap-2">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
className="flex-1 px-4 py-2 border rounded-lg"
placeholder="Type a message..."
disabled={!isConnected}
/>
<button
type="submit"
disabled={!isConnected || !inputValue.trim()}
className="px-6 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50"
>
Send
</button>
</form>
</div>
);
}Key Concepts Demonstrated
WebSocket Connection
Establishing and maintaining real-time connections between client and server.
Event-driven Communication
Using Socket.IO events for efficient message passing and real-time updates.
Broadcasting
Sending messages to all connected clients simultaneously for real-time collaboration.
State Management
Managing connection state and message history with React hooks and TypeScript.
Getting Started
Ready to build your own real-time application? Follow these steps to get the WebSocket tutorial running on your local machine:
Prerequisites
- • Node.js (version 18 or higher)
- • npm or yarn
Installation Steps
- 1Clone the repository:
git clone https://github.com/audoir/websocket-tutorial.git - 2Navigate to the project directory:
cd websocket-tutorial - 3Install dependencies:
npm install - 4Start the development server:
npm run dev - 5Open your browser and navigate to:
Testing the Application
To see the real-time capabilities in action, follow these steps:
- 1. Open multiple browser tabs/windows to http://localhost:3000
- 2. Type a message in one tab and press Send
- 3. Observe the message appearing in all other tabs in real-time
- 4. Notice the connection status indicator
- 5. Try refreshing a tab to see reconnection behavior
Available Scripts
npm run devStart development server with hot reloadnpm run buildBuild the application for productionnpm startStart the production servernpm run lintRun ESLint for code quality checksLearning Objectives
By completing this tutorial, you will have gained hands-on experience with:
- • Setting up a WebSocket server with Socket.IO
- • Integrating WebSocket communication in a React application
- • Managing real-time state updates with custom hooks
- • Building a responsive chat interface with Tailwind CSS
- • Handling connection states and error scenarios
- • TypeScript best practices for real-time applications
- • Event-driven architecture patterns
- • Broadcasting messages to multiple clients
Production Considerations
When deploying WebSocket applications to production, consider these important factors:
Conclusion
WebSockets open up a world of possibilities for building interactive, real-time applications. By providing persistent, bidirectional communication channels, they enable experiences that were previously impossible or impractical with traditional HTTP-based architectures.
The chat application tutorial demonstrates fundamental patterns that you can apply to build various real-time features: live notifications, collaborative editing, real-time dashboards, multiplayer games, and much more. The combination of Socket.IO, React, and TypeScript provides a robust foundation for production-ready real-time applications.
About the Author
Wayne Cheng is the founder and AI app developer at Audoir, LLC. Prior to founding Audoir, he worked as a hardware design engineer for Silicon Valley startups and an audio engineer for creative organizations. He holds an MSEE from UC Davis and a Music Technology degree from Foothill College.
Further Exploration
To continue your WebSocket journey, explore the complete tutorial repository and experiment with extending the chat application. Consider adding features like user authentication, message persistence, file sharing, or video calling to deepen your understanding of real-time communication.
For more AI-powered development tools and tutorials, visit Audoir .