Back

Build a ChatBot with ChatGPT and React

Build a ChatBot with ChatGPT and React

As AI technology advances, chatbots have become a powerful tool for improving user interactions. By combining ChatGPT with user-friendly frameworks like React, developers can create intelligent and dynamic chatbots that engage users in natural and meaningful conversations. This article will show you how to create an AI chatbot using ChatGPT and React, setting up the development environment, customizing the chatbot’s user interface, and managing responses from the ChatGPT API.

Let’s start with the setup. This involves importing dependencies and installing the tools and software the app will need.

Some tools and software that we will use to build the simple chatbot are:

  1. Node.js and npm: Install Node.js, which includes npm (Node Package Manager), from the official website. npm will be used to manage dependencies and run scripts for the project.

  2. Create a new React App using Vite: Open your terminal and create a new React app by running the command below

npm create vite@latest app -- --template react

Navigate to the app directory using this command in your terminal:

cd app

Then, run:

npm install
  1. Install chat-ui-kit-react: This is a UI kit library for building chat interfaces in React applications. To install it, use the following command:
npm install @chatscope/chat-ui-kit-react

To learn more about the chat-ui-kit-react library, you can read the documentation here

We’ll also require some dependencies that will be used in styling the components. The dependencies will be imported on the App.jsx file. You can find it by

  • Expanding the app folder.
  • Clicking on the src folder.
  • Navigating to App.jsx.
  • On App.jsx, delete the template and leave only the parent element. This is what the App.jsx file should look like.
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {

 return (
   <>      
   </>
 )
}

export default App

Import the following components:

import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
import { MainContainer, ChatContainer, MessageList, Message, MessageInput, TypingIndicator } from '@chatscope/chat-ui-kit-react'

Creating Chat UI

chat-ui-kit-react provides components to create a chat interface. We will build the components that include all our chat functionalities in our App.jsx file.

return (
 <>
   {/* A container for the chat window */}
   <div style={{ position: "relative", height: "100vh", width: "700px" }}>
     {/* All components are wrapped in the MainContainer */}
     <MainContainer>
       {/* All chat logic will be contained in the ChatContainer */}
       <ChatContainer>
         {/* Shows all our messages */}
         <MessageList></MessageList>
       </ChatContainer>
     </MainContainer>
   </div>
 </>
);

Further styling can be applied by editing the Index.css file or adding inline styles.

Manage Messages State

The MessageList component is used to display all of our various messages. We need to store these messages in an array using useState and initialize it with one message.

// State to store chat messages
const [chatMessages, setChatMessages] = useState([
 {
   message: "Hello, I am ChatGPT!",
   sender: "ChatGPT",
 },
]);

To display these messages in our UI, we can achieve this by mapping over the array and rendering each message.

<MessageList>
 {/* Map through chat messages and render each message */}
 {chatMessages.map((message, i) => {
   return <Message key={i} model={message} />;
 })}
</MessageList>;

For a better layout, we can apply some styling to messages from ChatGPT so that the responses are aligned to the left.

<MessageList>
 {/* Map through chat messages and render each message */}
 {chatMessages.map((message, i) => {
   return (
     <Message
       key={i}
       model={message}
       style={message.sender === "ChatGPT" ? { textAlign: "left" } : {}}
     />
   );
 })}
</MessageList>;

We can view our Chatbot by running this command on the terminal.

npm run dev

Our UI should look like this: -

Handling user input

MessageInput component enables users to input messages for processing. Additionally, we can implement a function named handleUserMessage to manage the user’s entered messages. This component should be added after the MessageList component.

<MessageInput placeholder='Type Message here' onSend={handleUserMessage}/>

Now, let’s create the handleUserMessage function. This function will serve two roles:

  1. Creating a new user object that will contain: i. The message from the user. ii. A description of the user as the sender. iii. Indication of the message direction as outgoing. This means the message will be displayed on the left side of the chat UI.
  2. Updating chat messages with the new user message.
// Function to handle user messages
const handleUserMessage = async (userMessage) => {
 // Create a new user message object
 const newUserMessage = {
   message: userMessage,
   sender: "user",
   direction: "outgoing",
 };

 // Update chat messages state with the new user message
 const updatedChatMessages = [...chatMessages, newUserMessage];
 setChatMessages(updatedChatMessages);
};

Set Up ChatGPT API

To set up ChatGPT API to process user messages, we can follow these steps:

Obtaining an API key from OpenAI

  • Visit the OpenAI website and create an account.
  • Log in to your account and click on your profile name or icon to access the menu.
  • From the menu, select the ‘View API Keys’ option.
  • Click on the ‘Create New Secret Key’ button.
  • Once the API key is generated, copy and paste it somewhere safe. Note: The key will not be displayed again, so store it securely.

Setting up ChatGPT API access

We can store the API key as a variable above the App function.

const OPENAI_API_KEY = "YOUR API KEY"; //Replace with your generated openai api key

Process Message with ChatGPT API

After the handleUserMessage function, we will create an asynchronous function called processUserMessageToChatGPT. This function captures the user’s message, sends it to ChatGPT, and returns a response from ChatGPT. The steps involved are as follows:

  1. Preparing the messages in the required format for the API: Messages are categorized into three roles: User, Assistant, and System. “User” represents messages input by the user, and “Assistant” refers to messages from ChatGPT.
async function processUserMessageToChatGPT(messages) {
 // Prepare the messages in the required format for the API
 let apiMessages = messages.map((messageObject) => {
   let role = "";
   if (messageObject.sender === "ChatGPT") {
     role = "assistant";
   } else {
     role = "user";
   }
   return { role: role, content: messageObject.message };
 });
}
  1. Create a System message for ChatGPT: A system message is a special message that provides instructions or context to the language model before processing user messages. It helps guide the behavior and response of the model during the conversation. For instance, a system message could include instructions like:
  • “Explain all concepts like Shakespeare.”
  • “You are a customer support representative for a tech company.” You can improve the system message prompt for specific chatbot use cases across different industries, such as Travel, Technology, etc.
// System message for ChatGPT
const systemMessage = {
 role: "system",
 content: "Explain all concept like a Professor in Biochemistry",
};
  1. Prepare the API request body: This is used as a container for the conversation messages and system instructions, and it is sent to the OpenAI API for processing by the ChatGPT model.
const apiRequestBody = {
 model: "gpt-3.5-turbo",
 messages: [
   systemMessage, // System message should be in front of user messages
   ...apiMessages,
 ],
};
  1. Send the user message to ChatGPT API and return the response.
await fetch("https://api.openai.com/v1/chat/completions", {
 method: "POST",
 // Set the request headers, including the authorization, using the API key.
 headers: {
   Authorization: "Bearer " + OPENAI_API_KEY, // Replace OPENAI_API_KEY with the actual API key.
   "Content-Type": "application/json",
 },
 // Convert the API request body object into a JSON string and send it as the request body.
 body: JSON.stringify(apiRequestBody),
})
 // Once the API response is received, convert it to JSON format.
 .then((data) => {
   return data.json();
 })
 // After converting to JSON, extract the chatbot's response message from the API response.
 .then((data) => {
   // Update the chat messages.
   setChatMessages([
     ...messages,
     {
       message: data.choices[0].message.content,
       sender: "ChatGPT",
     },
   ]);
 });

Note: Remember to include a space after “Bearer” in the Authorization header; otherwise, API calls may fail.

We can now call the processUserMessageToChatGPT function in the handleUserMessage function.

await processUserMessageToChatGPT(updatedChatMessages);

Managing loading states while waiting for the API response

We can set a typing indicator to display while the chatbot is processing a response. Firstly, we’ll create a state to indicate typing and set it to false.

// State to manage the typing indicator of the chatbot
const [isChatbotTyping, setIsChatbotTyping] = useState(false);

In the handleUserMessage function, you can set the indicator to true. This line should be added before calling the processUserMessageToChatGPT function.

// Set the typing indicator for the chatbot
setIsChatbotTyping(true);

Once the response has been received from ChatGPT, within the processUserMessageToChatGPT function, you can set the state to false after updating the chat messages.

// Set the typing indicator to false after getting the response
setIsChatbotTyping(false);

You can display the indicator in the UI when it’s true by utilizing the typingIndicator property.

<MessageList
 // Display a typing indicator when the chatbot is processing a response.
 typingIndicator={
   isChatbotTyping ? <TypingIndicator content="ChatGPT is thinking" /> : null
 }
>
 {/* Map through chat messages and render each message */}
 {chatMessages.map((message, i) => {
   return (
     // Render each individual chat message as a <Message> component.
     <Message
       key={i}
       model={message}
       // Add additional styling to align the chatbot's response to the left.
       style={message.sender === "ChatGPT" ? { textAlign: "left" } : {}}
     />
   );
 })}
</MessageList>;

The Final App.jsx file should look like this:

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
import {
 MainContainer,
 ChatContainer,
 MessageList,
 Message,
 MessageInput,
 TypingIndicator,
} from "@chatscope/chat-ui-kit-react";

const OPENAI_API_KEY = "YOUR API KEY";

function App() {
 // State to manage the typing indicator of the chatbot
 const [isChatbotTyping, setIsChatbotTyping] = useState(false);

 // State to store chat messages
 const [chatMessages, setChatMessages] = useState([
   {
     message: "Hello, I am ChatGPT!",
     sender: "ChatGPT",
   },
 ]);

 // Function to handle user messages
 const handleUserMessage = async (userMessage) => {
   // Create a new user message object
   const newUserMessage = {
     message: userMessage,
     sender: "user",
     direction: "outgoing",
   };

   // Update chat messages state with the new user message
   const updatedChatMessages = [...chatMessages, newUserMessage];
   setChatMessages(updatedChatMessages);

   // Set the typing indicator for the chatbot
   setIsChatbotTyping(true);

   // Process user message with ChatGPT
   await processUserMessageToChatGPT(updatedChatMessages);
 };

 // Function to send the user message to ChatGPT API
 async function processUserMessageToChatGPT(messages) {
   // Prepare the messages in the required format for the API
   let apiMessages = messages.map((messageObject) => {
     let role = "";
     if (messageObject.sender === "ChatGPT") {
       role = "assistant";
     } else {
       role = "user";
     }
     return { role: role, content: messageObject.message };
   });

   // System message for ChatGPT
   const systemMessage = {
     role: "system",
     content: "Explain all concept like a Professor in Biochemistry",
   };

   // Prepare the API request body
   const apiRequestBody = {
     model: "gpt-3.5-turbo",
     messages: [
       systemMessage, // System message should be in front of user messages
       ...apiMessages,
     ],
   };

   // Send the user message to ChatGPT API
   await fetch("https://api.openai.com/v1/chat/completions", {
     method: "POST",
     headers: {
       Authorization: "Bearer " + OPENAI_API_KEY,
       "Content-Type": "application/json",
     },
     body: JSON.stringify(apiRequestBody),
   })
     .then((data) => {
       return data.json();
     })
     .then((data) => {
       // Update chat messages with ChatGPT's response
       setChatMessages([
         ...messages,
         {
           message: data.choices[0].message.content,
           sender: "ChatGPT",
         },
       ]);
       // Set the typing indicator to false after getting the response
       setIsChatbotTyping(false);
     });
 }

 return (
   <>
     {/* A container for the chat window */}
     <div style={{ position: "relative", height: "100vh", width: "700px" }}>
       <MainContainer>
         <ChatContainer>
           {/* Display chat messages and typing indicator */}
           <MessageList
             typingIndicator={
               isChatbotTyping ? (
                 <TypingIndicator content="ChatGPT is thinking" />
               ) : null
             }
           >
             {/* Map through chat messages and render each message */}
             {chatMessages.map((message, i) => {
               return (
                 <Message
                   key={i}
                   model={message}
                   style={
                     message.sender === "ChatGPT" ? { textAlign: "left" } : {}
                   }
                 />
               );
             })}
           </MessageList>
           {/* Input field for the user to type messages */}
           <MessageInput
             placeholder="Type Message here"
             onSend={handleUserMessage}
           />
         </ChatContainer>
       </MainContainer>
     </div>
   </>
 );
}

export default App;

Your AI ChatBot, with its full functionality, is now complete.

-

Common Errors and How to Fix Them

Errors may occur during the installation of the chat-ui-kit-react library. You may encounter errors such as:

npm ERR! code 1
npm ERR! path C:\Users\work\Documents\vs code\app\node_modules\esbuild
npm ERR! command failed
npm ERR! command C:\Windows\system32\cmd.exe /d /s /c node install.js
npm ERR! node:child_process:828
npm ERR!     err = new Error(msg);
npm ERR!           ^
npm ERR!
npm ERR! Error: Command failed: C:\Program Files\nodejs\node.exe 

Here’s how you can resolve it:

  • Remove the node modules
rmdir /s /q node_modules
  • Install the latest npm
npm install -g npm@latest  
  • Finally, reinstall the package
npm install @chatscope/chat-ui-kit-react

Errors may also occur when making requests to the API. This is the documentation on the meaning of the different errors you may encounter.

Conclusion

Setting up the development environment, Creating the chatbot user interface, and handling user messages using the ChatGPT API are vital steps in building an effective chatbot. With the power of AI, chatbots can significantly enhance user interactions and customer service in various industries.

So, go ahead and explore the world of AI chatbots with ChatGPT and React, and unlock new possibilities for seamless and interactive user engagements!

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay