Building Real-Time Apps with Next.js and WebSockets
Tags: webdev, javascript, tutorial, nextjs
Published: 2024-07-05 19:18:06

## Introduction

In today's fast-paced digital world, real-time data exchange is essential for developing dynamic and interactive web applications. WebSockets are a powerful method for enabling real-time, bidirectional communication between clients and servers. In this blog post, we'll look at how to use WebSockets with Next.js to create real-time apps.

A WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. It enables a real-time, event-driven connection between a client and a server.

Unlike traditional HTTP software, which follows a request-response model, WebSockets allow two-way (bi-directional) communication. This means that the client and the server can send data to each other anytime without continuous polling. To learn more about WebSocket check [here](https://www.geeksforgeeks.org/what-is-web-socket-and-how-it-is-different-from-the-http/). Without further ado, let's get started developing our real-time communication application. We'll start by bootstrapping a new Next.js project. 

Open your preferred code editor and run the following command on the terminal to create a new project.

```
 npx create-next-app@latest real-time-app
```
Make sure to setup the project as shown below.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lqyoju23l60hvbvzjv7n.PNG)

After it has finished, run the following commands to open the projects folder in the code editor.

```
cd real-time-app
code .
```

## Installing dependencies 

Run the following command to install `ws`  a WebSocket library for Node.js.

```
npm install ws
```

## Creating a WebSocket server

Let's start by creating `server.js` file in the root directory and insert the following code in it.

```
 const { createServer } = require('http');
 const { parse } = require('url');
 const next = require('next');
 const WebSocket = require('ws');

 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 wss = new WebSocket.Server({ server });

     wss.on('connection', (ws) => {
         console.log('New client connected');

         ws.on('message', (message) => {
             console.log(`Received message: ${message}`);
             ws.send(`Server: ${message}`);
         });

         ws.on('close', () => {
             console.log('Client disconnected');
         });
     });

     server.listen(3000, (err) => {
         if (err) throw err;
         console.log('> Ready on http://localhost:3000');
     });
 });
```

In this code, we set up a server. It first imports the necessary modules and sets up a Next.js app in development mode if NODE_ENV is not set to 'production'. The app prepares the server to handle HTTP requests with Next.js's request handler. It also creates a WebSocket server (wss) attached to the HTTP server.

When a WebSocket client connects, the server logs the connection. It listens for messages from the client, logs them, and sends back a response. It also logs when the client disconnects. The server listens on port 3000.

Next, modify the Script in the package.json file to use the custom server we've just created.

```
  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
},
```

## Integrating WebSocket client in Next.js

In this part, we will create a WebSocket client in our Next.js application. Begin by creating a hooks folder in the app directory.
Then, create a file called `useWebSocket.js`.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c9o8s2kpbdki3r2xv9i4.PNG)

Insert the following code in that file

```
 import { useEffect, useState } from 'react';

 const useWebSocket = (url) => {
     const [messages, setMessages] = useState([]);
     const [ws, setWs] = useState(null);

     useEffect(() => {
         const socket = new WebSocket(url);
         setWs(socket);

         socket.onmessage = (event) => {
             setMessages((prevMessages) => [...prevMessages, event.data]);
         };

         return () => {
             socket.close();
         };
     }, [url]);

     const sendMessage = (message) => {
         if (ws) {
             ws.send(message);
         }
     };

     return { messages, sendMessage };
 };

 export default useWebSocket;
```

In this code we define a custom React hook `useWebSocket` for managing WebSocket connections. It establishes a connection to a WebSocket server using the provided URL, stores incoming messages in a state array, and provides a function to send messages through the WebSocket.

`useEffect`: Opens a WebSocket connection when the component mounts and closes it when the component unmounts.

`onmessage`: Updates the messages state with incoming messages.

`sendMessage`: Sends a message through the WebSocket if it's connected.
The hook returns the messages array and the sendMessage function for use in components.

## Using the WebSocket in a component

To use WebSocket in our component, update the `page.js` file with the following code.


```
'use client'

import { useState } from 'react';
import useWebSocket from '@/app/hooks/useWebSocket';

const Home = () => {
    const { messages, sendMessage } = useWebSocket('ws://localhost:4000');
    const [input, setInput] = useState('');

    const handleSubmit = (e) => {
        e.preventDefault();
        sendMessage(input);
        setInput('');
    };

    return (
        <div>
            <h1>Real-time Chat</h1>
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                />
                <button type="submit">Send</button>
            </form>
            <div>
                {messages.map((msg, index) => (
                    <div key={index}>{msg}</div>
                ))}
            </div>
        </div>
    );
};

export default Home;
```

This is a React component for our real-time chat functionality using WebSockets. The component leverages React's `useState` hook to manage the state of the input field, allowing users to type and send messages.

**Key Features:**

- 
WebSocket Integration: Utilizes a custom useWebSocket hook to establish a WebSocket connection to ws://localhost:4000.

- 
State Management: Uses useState to handle the current message input.

- 
Message Handling: The handleSubmit function sends the typed message via WebSocket and then clears the input field.

- 
Real-time Updates: Displays received messages in real-time by mapping over the messages array.

**How It Works:**

- 
Connection: On component mount, useWebSocket initiates a WebSocket connection.

- 
Sending Messages: When the form is submitted, sendMessage sends the message, and the input field is reset.

- 
Displaying Messages: Received messages are displayed dynamically as they arrive.

## Running the application

Run the following command on the terminal to start the application. Open your browser and navigate to http://localhost:3000. You should see a simple chat interface where you can send messages and receive real-time updates.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dpkvsc202kgok1wdzexa.PNG)

## Conclusion 

Finally, developing real-time applications with Next.js and WebSockets is an effective way to improve user experiences through instant communication and updates. This post demonstrates how to set up a WebSocket server and a Next.js application to create a smooth real-time chat interface. Using these technologies, developers may incorporate dynamic, interactive features into their online applications, paving the way for more engaging and responsive user engagements. This method not only increases functionality but also lays the groundwork for future real-time application development.