Published: 28 October 2024
Introduction
Building real-time applications like a chat app requires a strong foundation in both frontend and backend technologies. In this tutorial, we will be constructing a real-time chat app using Next.js, WebSockets, and Server-Side Rendering (SSR). The goal is to leverage SSR for fast initial page loads while using WebSockets for seamless real-time communication between clients.
By the end of this guide, you will have a fully functional real-time chat app that works smoothly across devices, providing a great user experience with low latency and high performance.
Prerequisites
Before we dive into the code, here’s what you should be familiar with:
- JavaScript and React (since Next.js is built on React)
- Basic knowledge of Next.js and its routing system
- WebSockets and their use for real-time communication
- Experience with server-side rendering (SSR) and how it works in Next.js
You should also have the following installed:
- Node.js (at least version 14.x)
- A text editor (like VS Code)
- npm or yarn
If you don’t have these set up already, head over to the official documentation to install them.
Step 1: Setting Up the Next.js Project
Let’s begin by creating a new Next.js application. In your terminal, run the following command:
npx create-next-app@latest real-time-chat
Once the app is created, navigate into the project directory:
cd real-time-chat
Now, install the required dependencies for WebSockets:
npm install socket.io-client socket.io
- socket.io-client will be used in the frontend to establish the WebSocket connection.
- socket.io will be used on the server to manage WebSocket connections.
Step 2: Creating the WebSocket Server
To enable real-time communication, we’ll need to set up a WebSocket server. Next.js supports custom servers, so we’ll use Socket.io to handle WebSocket connections within a custom server.
- In the root of your project, create a new file named server.js.
touch server.js
- Inside server.js, set up the custom server with Socket.io.
const express = require('express');
const next = require('next');
const http = require('http');
const socketIo = require('socket.io');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
const httpServer = http.createServer(server);
const io = socketIo(httpServer);
// WebSocket events
io.on('connection', (socket) => {
console.log('A user connected');
// Listen for messages from clients
socket.on('sendMessage', (msg) => {
io.emit('receiveMessage', msg); // Broadcast message to all clients
});
socket.on('disconnect', () => {
console.log('A user disconnected');
});
});
// Next.js request handler
server.all('*', (req, res) => {
return handle(req, res);
});
httpServer.listen(3001, (err) => {
if (err) throw err;
console.log('> Ready on http://localhost:3001');
});
});
- Add the
server.js
script to the package.json underscripts
:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "next start"
}
With this setup, we have a Socket.io WebSocket server running on port 3001
, which will listen for messages and broadcast them to connected clients.
Step 3: Creating the Real-Time Chat Frontend
Now, let’s build the frontend part of the chat application. We’ll use React and WebSockets to handle sending and receiving messages.
1. Setting Up WebSocket Client
First, we need to create a WebSocket client using the socket.io-client package. In the pages
directory, create a new file called chat.js.
touch pages/chat.js
In this file, set up the WebSocket connection and manage messages with React hooks.
import { useEffect, useState } from 'react';
import io from 'socket.io-client';
let socket;
const Chat = () => {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
// Connect to WebSocket on mount
useEffect(() => {
socket = io('http://localhost:3001');
// Listen for incoming messages
socket.on('receiveMessage', (msg) => {
setMessages((prevMessages) => [...prevMessages, msg]);
});
return () => {
socket.disconnect();
};
}, []);
const handleSendMessage = () => {
if (newMessage.trim()) {
// Emit the new message to the server
socket.emit('sendMessage', newMessage);
setNewMessage(''); // Clear the input
}
};
return (
<div style={{ maxWidth: 600, margin: 'auto', padding: 20 }}>
<h1>Real-Time Chat</h1>
<div style={{ marginBottom: 20, maxHeight: 300, overflowY: 'auto' }}>
{messages.map((message, index) => (
<div key={index} style={{ padding: 10, borderBottom: '1px solid #ddd' }}>
{message}
</div>
))}
</div>
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="Type a message"
style={{ padding: 10, width: '80%' }}
/>
<button onClick={handleSendMessage} style={{ padding: 10, marginLeft: 10 }}>
Send
</button>
</div>
);
};
export default Chat;
Explanation:
- The
useEffect
hook connects to the WebSocket server when the component mounts and sets up a listener for incoming messages usingsocket.on('receiveMessage')
. socket.emit('sendMessage', message)
sends the typed message to the server.setMessages
updates the state to render the received messages on the page.
Step 4: Server-Side Rendering (SSR) with Next.js
Now, let’s focus on the Server-Side Rendering (SSR) feature of Next.js. In this case, we will pre-render the chat page to improve performance and SEO. While WebSockets don’t rely on SSR for real-time messaging, SSR can still be beneficial for delivering the chat app’s initial HTML quickly.
Modify pages/chat.js to support SSR by exporting an async
function, getServerSideProps
, which will provide server-side data (if needed) to the page.
export async function getServerSideProps() {
// You could fetch some data here, if needed
// For instance, a list of users, or previous messages stored in a database
return {
props: {}, // This could be any data you want to pass to the page
};
}
Step 5: Running the App
Now that we’ve set up both the backend and frontend, let’s run the application.
- First, start the server:
npm run dev
- Open http://localhost:3001/chat in your browser to see the chat app in action. You can open the app in multiple tabs to simulate real-time communication between users.
Conclusion
You’ve just built a real-time chat app with Next.js, WebSockets, and Server-Side Rendering. In this tutorial, we covered:
- Setting up a custom WebSocket server with Socket.io.
- Building a React-based chat interface to send and receive messages.
- Integrating SSR in Next.js to improve performance for the initial load.
This is just the beginning. You can take this app further by adding features like user authentication, message history, and push notifications. You can also explore deploying your app using Vercel for SSR and Socket.io on platforms like Heroku or AWS for production.
By combining real-time communication with server-side rendering, you create an application that is both performant and interactive. Happy coding!