Web Development

Building a Real-Time Chat Application with Next.js and WebSockets

Learn how to build a modern real-time chat application using Next.js, WebSockets, and React. Complete with user authentication, message persistence, and typing indicators.

Dev ND
September 17, 2025
15 min read
458 views
Building a Real-Time Chat Application with Next.js and WebSockets
# Building a Real-Time Chat Application with Next.js and WebSockets Real-time communication is essential for modern web applications. In this tutorial, we'll build a full-featured chat application using Next.js and WebSockets. ## Project Overview Our chat application will include: - Real-time messaging - User authentication - Message persistence - Typing indicators - Online user status - Message history ## Setting Up the Project ```bash npx create-next-app@latest chat-app cd chat-app npm install socket.io socket.io-client next-auth ``` ## WebSocket Server Setup Create a custom server for handling WebSocket connections: ```javascript // server.js const { createServer } = require('http'); const { parse } = require('url'); const next = require('next'); const { Server } = require('socket.io'); const dev = process.env.NODE_ENV !== 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); app.prepare().then(() => { const server = createServer((req, res) => { const parsedUrl = parse(req.url, true); handle(req, res, parsedUrl); }); const io = new Server(server); io.on('connection', (socket) => { console.log('User connected:', socket.id); socket.on('join-room', (roomId) => { socket.join(roomId); }); socket.on('send-message', (data) => { socket.to(data.roomId).emit('receive-message', data); }); socket.on('typing', (data) => { socket.to(data.roomId).emit('user-typing', data); }); socket.on('disconnect', () => { console.log('User disconnected:', socket.id); }); }); server.listen(3000, (err) => { if (err) throw err; console.log('Server running on http://localhost:3000'); }); }); ``` ## Client-Side Implementation Create the chat component: ```tsx 'use client'; import { useState, useEffect, useRef } from 'react'; import io, { Socket } from 'socket.io-client'; interface Message { id: string; text: string; userId: string; username: string; timestamp: Date; } export default function ChatRoom({ roomId }: { roomId: string }) { const [socket, setSocket] = useState(null); const [messages, setMessages] = useState([]); const [newMessage, setNewMessage] = useState(''); const [isTyping, setIsTyping] = useState(false); const [typingUsers, setTypingUsers] = useState([]); const messagesEndRef = useRef(null); useEffect(() => { const socketInstance = io(); setSocket(socketInstance); socketInstance.emit('join-room', roomId); socketInstance.on('receive-message', (message: Message) => { setMessages(prev => [...prev, message]); }); socketInstance.on('user-typing', (data) => { setTypingUsers(prev => [...prev, data.username]); setTimeout(() => { setTypingUsers(prev => prev.filter(user => user !== data.username)); }, 3000); }); return () => { socketInstance.disconnect(); }; }, [roomId]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); const sendMessage = () => { if (newMessage.trim() && socket) { const message: Message = { id: Date.now().toString(), text: newMessage, userId: 'current-user', username: 'Current User', timestamp: new Date(), }; socket.emit('send-message', { ...message, roomId }); setMessages(prev => [...prev, message]); setNewMessage(''); } }; const handleTyping = () => { if (socket && !isTyping) { setIsTyping(true); socket.emit('typing', { roomId, username: 'Current User' }); setTimeout(() => setIsTyping(false), 3000); } }; return (
{messages.map((message) => (

{message.username}

{message.text}

{new Date(message.timestamp).toLocaleTimeString()}

))} {typingUsers.length > 0 && (
{typingUsers.join(', ')} {typingUsers.length === 1 ? 'is' : 'are'} typing...
)}
{ setNewMessage(e.target.value); handleTyping(); }} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} placeholder="Type your message..." className="flex-1 border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" />
); } ``` ## Advanced Features ### Message Persistence Store messages in a database: ```javascript // In your socket handler socket.on('send-message', async (data) => { // Save to database await saveMessage(data); // Broadcast to room socket.to(data.roomId).emit('receive-message', data); }); ``` ### User Authentication Integrate with NextAuth.js for secure user management. ### File Sharing Add support for image and file uploads. ## Deployment Considerations - Use Redis for scaling across multiple servers - Implement rate limiting to prevent spam - Add message encryption for security - Monitor WebSocket connections and performance ## Conclusion Building real-time features with Next.js and WebSockets opens up many possibilities for interactive applications. Start with this foundation and add features based on your specific needs.
Related Posts
You might also be interested in these posts