Rabikant

Posted on November 23rd

How to build realtime chatroom with WebSocket using NextJS

"Lets learn How to build realtime chatroom with WebSocket using NextJS"

Let's create our app with:

npx create-next-app@latest example-app

Then cd into your app with:

cd example-app

Then name your app and configure it however you like.

After that, let's start by installing piesocket dependency:

npm i piesocket-js@5

Now let's install UUID

npm i uuid

Create an account on piehost.com after that you will be able to create a cluster there and get your api key and cluster id.

Importing modules and setup

This code imports the necessary modules for our app:

'use client';

import Head from 'next/head';
import { useState, useEffect } from 'react';
import PieSocket from 'piesocket-js';
import { v4 as uuidv4 } from 'uuid';
const ChatApp = () => {
};

next/head for the HTML head tags.

react for using React Hooks (useState, useEffect)

PieSocket for dealing with Real time communication

uuid for creating unique ID’s for the usernames.

The ChatApp component is defined as a function component.

Setting up state and PieSocket

const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
const [channel, setChannel] = useState(null);
const [username, setUsername] = useState(`User_${uuidv4().slice(0, 8)}`);
var pieSocket = new PieSocket({
clusterId: "your cluster id",
apiKey: "your api key",
notifySelf: true
});

This code sets up the initial state of the component using React's useState hook:

messages: A empty array for storing chats.

newMessage: a new empty array to hold the new message that is being typed.

channel: is set to null initially but will be set to the PieSocket channel momentarily

username: created using uuidv4 and it was assigned a username ( e.g., User_12345678).

The pieSocket instance is created with the required configuration:

clusterId: name of the PieSocket cluster

apiKey: the API key for the cluster of PieSocket

notifySelf: a flag to allow the current user to be notified by the system of his/her messages.

Setting up PieSocket

useEffect(() => {
pieSocket.subscribe("chat-room").then((channel) => {
console.log("Channel is ready");
setChannel(channel);
channel.listen("new_message", (data, meta) => {
console.log("New message: ", data);
setMessages((prevMessages) => [...prevMessages, data]);
});
});
}, []);

This code sets up the PieSocket channel using the useEffect hook:

On the initialization of the component, it connects to the “chat-room” channel utilizing pieSocket. subscribe

Once the channel is ready, it makes ‘channel’ to be equal to the name of the subscribed channel.

It then listens for new messages on the channel using channel.listen

When a new message is received, it alters the messages state by adding the new message to an existing array.

The third argument to the useEffect hook is an empty array [], so it will run the cleanup function only once, when the component is mounted.

Handling send message functionality

const handleSendMessage = () => {
if (!channel) return;
channel.publish("new_message", {
message: newMessage,
sender: username
});
setNewMessage('');
};

This code defines the handleSendMessage function, which is called when the user clicks the "Send" button:

It checks whether a channel is opened before running it.

If available, it publishes a new message to the channel using channel.publish

The newMessage text and the actions that have been made, as well as the username of the sender is included in the message object.

Finally, the code sets the newMessage state to the empty string once the message is sent as a submission to the server.

Rendering the chat interface

return (
<div className="h-screen flex flex-col items-center h-screen justify-end bg-gray-100">
<Head>
<title>Chat App</title>
</Head>
<ul className="list-none mb-4 flex flex-col items-center justify-center m-8">
{messages.map((message, index) => (
<li key={index} className="flex flex-wrap mb-2">
<span className="text-gray-700">{message.sender}: {message.message}</span>
</li>
))}
</ul>
<div className="flex justify-center mb-4">
  <input
    type="text"
    value={newMessage}
    onChange={(e) => setNewMessage(e.target.value)}
    placeholder="Type a message..."
    className="bg-white rounded-md p-2 text-gray-700"
  />
  <button
    onClick={handleSendMessage}
    className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md ml-2"
  >
    Send
  </button>
</div>
</div>
);

This code returns the JSX elements that make up the chat interface:

The outermost container div with a gray color and with flex box used to layout the pieces.

Having a Head component to stipulate the page title

An Ul element to produce the layout of the chat messages.

Concrete each message is presented as the list item (li) with the username of the sender and the text of the message

An input field for typing new messages with type property value = newMessage and onChange event handler = handleChange

A “Send” button the execution of which leads to the call of the handleSendMessage function.

The chat interface is divided into three main sections: The labels of the fields incorporated in the modern messaging scheme are the message list, input field, and the send button.

This is the whole code for the app:

'use client';

import Head from 'next/head';
import { useState, useEffect } from 'react';
import PieSocket from 'piesocket-js';
import { v4 as uuidv4 } from 'uuid'; 

const ChatApp = () => {
  const [messages, setMessages] = useState([]);
  const [newMessage, setNewMessage] = useState('');
  const [channel, setChannel] = useState(null);
  const [username, setUsername] = useState(`User_${uuidv4().slice(0, 8)}`);
  var pieSocket = new PieSocket({
  clusterId: "your cluster id",
    apiKey: "your api key",
    notifySelf: true
  });

  useEffect(() => {
    pieSocket.subscribe("chat-room").then((channel) => {
      console.log("Channel is ready");
      setChannel(channel);
      channel.listen("new_message", (data, meta) => {
        console.log("New message: ", data);
        setMessages((prevMessages) => [...prevMessages, data]);
      });
    });
  }, []);

  const handleSendMessage = () => {
    if (!channel) return;
    channel.publish("new_message", {
      message: newMessage,
      sender: username
    });
    setNewMessage('');
  };

  return (
    <div className="h-screen flex flex-col items-center justify-end bg-gray-100">
      <Head>
        <title>Chat App</title>
      </Head>
      <ul className="list-none mb-4 flex flex-col items-center justify-center m-8">
        {messages.map((message, index) => (
          <li key={index} className="flex flex-wrap mb-2">
            <span className="text-gray-700">{message.sender}: {message.message}</span>
          </li>
        ))}
      </ul>
      <div className="flex justify-center mb-4">
        <input
          type="text"
          value={newMessage}
          onChange={(e) => setNewMessage(e.target.value)}
          placeholder="Type a message..."
          className="bg-white rounded-md p-2 text-gray-700"
        />
        <button
          onClick={handleSendMessage}
          className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md ml-2"
        > 
          Send
        </button>
      </div>
    </div>
  );
};

export default ChatApp;

Add this code in page.js and run your app with:

npm run dev

Complete Code

The project is available on our GitHub :  https://github.com/piehostHQ/nextjs-ws-chatroom

Comments

Leave a comment.

Share your thoughts or ask a question to be added in the loop.