Building a Real-Time Chat App with Next.js and WebSockets
Introduction
Real-time communication has become a fundamental feature in modern web applications, from customer support chats to collaborative tools. While there are several ways to implement real-time functionality, WebSockets provide a persistent, bidirectional connection between the client and server, making them ideal for chat applications.
In this post, we’ll explore how to build a real-time chat application using Next.js and WebSockets. Next.js, with its hybrid rendering capabilities, is an excellent choice for building performant applications, while WebSockets enable seamless real-time updates. We’ll cover setting up a WebSocket server, integrating it with Next.js, and handling real-time messaging efficiently.
Setting Up the WebSocket Server
Before integrating WebSockets with Next.js, we need a WebSocket server to handle connections and message broadcasting. For this example, we’ll use Node.js with the ws
library, a lightweight WebSocket implementation.
First, install the required dependency:
npm install ws
Next, create a simple WebSocket server (server.js
):
const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { console.log('New client connected'); ws.on('message', (message) => { console.log(`Received: ${message}`); // Broadcast the message to all clients wss.clients.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); ws.on('close', () => { console.log('Client disconnected'); }); }); console.log('WebSocket server running on ws://localhost:8080');
This server listens for incoming connections on port 8080
. When a client sends a message, the server broadcasts it to all other connected clients. You can start the server by running node server.js
.
Integrating WebSockets with Next.js
Next.js supports API routes, which we can use to integrate WebSocket functionality. However, since WebSockets require a persistent connection, we’ll handle the client-side WebSocket logic in a React component.
First, create a new Next.js page (pages/chat.js
) and set up the WebSocket connection:
import { useState, useEffect } from 'react'; export default function Chat() { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [ws, setWs] = useState(null); useEffect(() => { // Initialize WebSocket connection const socket = new WebSocket('ws://localhost:8080'); socket.onopen = () => { console.log('Connected to WebSocket server'); setWs(socket); }; socket.onmessage = (event) => { const newMessage = event.data; setMessages((prev) => [...prev, newMessage]); }; socket.onclose = () => { console.log('Disconnected from WebSocket server'); }; return () => { if (socket) socket.close(); }; }, []); const sendMessage = () => { if (ws && input.trim()) { ws.send(input); setInput(''); } }; return ( <div> <h1>Real-Time Chat</h1> <div> {messages.map((msg, index) => ( <p key={index}>{msg}</p> ))} </div> <input type="text" value={input} onChange={(e) => setInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && sendMessage()} /> <button onClick={sendMessage}>Send</button> </div> ); }
This component establishes a WebSocket connection when mounted and listens for incoming messages. Users can send messages by typing into the input field and pressing Enter or clicking the Send button.
Handling Authentication and Rooms
In a production environment, you’ll likely need authentication and support for multiple chat rooms. Let’s extend our example to include these features.
1. Authentication
Modify the WebSocket server to handle authentication by checking a token when a client connects:
wss.on('connection', (ws, req) => { const token = req.url.split('token=')[1]; if (!token) { ws.close(1008, 'Unauthorized'); return; } // Validate token (e.g., using JWT) console.log(`Authenticated user with token: ${token}`); // Rest of the connection logic... });
On the client side, include the token in the WebSocket URL:
const socket = new WebSocket(`ws://localhost:8080?token=${userToken}`);
2. Chat Rooms
To support multiple rooms, modify the server to track rooms and broadcast messages only to clients in the same room:
const rooms = new Map(); wss.on('connection', (ws, req) => { const roomId = req.url.split('roomId=')[1]?.split('&')[0]; if (!roomId) { ws.close(1008, 'Room ID required'); return; } if (!rooms.has(roomId)) { rooms.set(roomId, new Set()); } const room = rooms.get(roomId); room.add(ws); ws.on('message', (message) => { room.forEach((client) => { if (client !== ws && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); ws.on('close', () => { room.delete(ws); if (room.size === 0) { rooms.delete(roomId); } }); });
Clients can now join a room by specifying the roomId
in the WebSocket URL:
const socket = new WebSocket(`ws://localhost:8080?roomId=${roomId}&token=${userToken}`);
Conclusion
Building a real-time chat app with Next.js and WebSockets is a powerful way to add interactive features to your applications. By setting up a WebSocket server and integrating it with Next.js, you can create a seamless real-time experience for users.
Key takeaways:
- Use the
ws
library to create a lightweight WebSocket server in Node.js. - Integrate WebSockets in Next.js using client-side logic and React hooks.
- Extend the basic implementation with authentication and room support for production use.
For larger-scale applications, consider using managed services like Socket.io or Pusher, which handle scalability and additional features out of the box. However, understanding the fundamentals of WebSockets will give you greater control over your real-time implementations.
Happy coding! 🚀