Create your own ChatGPT clone using React and OpenAI APIs

Updated 4 months ago on July 28, 2024

In this tutorial, we will look at how to create a custom Chatbot application that will allow us to ask questions and get quality answers. The bot will remember previous prompts, mimicking the conversation with contextualization.

Key findings

  1. Creating a chatbot using OpenAI ChatGPT and React : This tutorial provides step-by-step instructions for creating a custom chatbot application in React using OpenAI ChatGPT for advanced language processing. The chatbot remembers previous prompts, simulating a continuous, context-aware conversation.
  2. User Interface and Feature Implementation : The application has a user-friendly interface with components such as message box, input area, send button and conversation history. It demonstrates how to structure a React application, manage state and handle events to provide a seamless user experience.
  3. API call integration and data processing : The guide covers the process of making API calls to OpenAI's ChatGPT and processing data to display chatbot responses. It emphasizes the importance of protecting the API key, especially for production, and suggests improvements such as using Express for server-side operations and enabling persistent storage for conversation history.
A GIF animation showing our finished bot in action
Chatbots have become indispensable tools for companies and developers seeking to improve customer interactions and optimize the user experience in today's rapidly evolving digital landscape. OpenAI's ChatGPT chatbot has evolved from a cutting-edge experiment to a powerful chatbot development tool. Its meteoric rise to success is nothing short of astounding, captivating users around the world. The demo code for this project is available on CodeSandbox . To test the project live, you will have to specify your own OpenAI API key in the . env . To get it, create an account on OpenAI, log in, go to the API keys section and generate a new API key.

Scheduling features and user interface

Our application will be based on React , and we will use OpenAI API for data access and CSS modules for styling. Using React will allow us to create a dynamic and responsive user interface, which will improve the overall user experience. The OpenAI API will allow us to access advanced language processing capabilities, providing data to create insightful interactions. In addition, CSS modules will allow us to support modular design, facilitating efficient development and customization of the application. The features we will be implementing include:
  • A special input area where users will be able to create hints by suggesting contextually relevant queries.
  • Submit button , which will allow users to submit their requests to the API, initiating the conversation process.
  • Message elements to be displayed as chat-style messages in the conversation window, improving chat interactivity.
  • Message items to display ChatGPT responses that provide a conversational flow.
  • History function , which will list all recent user prompts. This will also allow users to go back to previous conversations.
  • The " Clear" button will allow you to delete the content you created, opening up a new page for new conversations.
The image below shows our component-based framework.
A wireframe of the app's interface
The entire application will be wrapped in a main container that will hold all the elements together. It will then be divided into two columns. The first column will contain all the messages from the user and ChatGPT. At the bottom of the column will be an input area and a button to send a message. The second column will contain the history of all recent prompts. At the bottom of the column will be Clear A button that will allow the user to erase the generated content.

Choice of color scheme

The design of the app prioritizes the ease of experiencing the content. This will allow us to provide several important benefits:
  • Users will be able to quickly grasp the information presented, leading to a more intuitive and user-friendly experience.
  • It will also increase accessibility, ensuring that people of different backgrounds and abilities can easily navigate and engage with the content.
The image below shows our color scheme.
Our five-color scheme: black, dark gray, lime-green, peach and white
The application background will be black, and the messages, history items, and input form will be dark gray. The text on the background of messages and input form will be white, which provides good contrast and makes the text easy to read. To give the application a twist, the column headings, Send The reply button and avatars will be in bright lime green colors. To emphasize Clear a soft red tone will be used. This will also help users avoid accidentally pressing the button.

Customizing a React application

We will use create-react-app to create our app. Run npx create-react-app react-chatgpt to create a new React project. Wait a minute for the setup to complete, and then change the working directory to the folder you just created using cd react-chatgpt and run npm start to start the developer server. This should open our project in the default browser. If it doesn't, go to http://localhost:3000 to open it manually. The React welcome screen should appear in front of us, as shown in the image below.
React welcome screen

Adding global styles

We will add a global style to create a unified visual appearance for all components of the application. Open index.css and include the following styling rules:
 @import url ("https://fonts.googleapis.com/css2?family=Varela+Round&display=swap" ) ; * { margin : 0 ; padding : 0 ; box-sizing : border-box ; font-family : "Varela Round", sans-serif ; } body { background-color : #121212 ; } 
First, we'll import the Varela Round font and customize the entire application to use it. We will also remove all the preset margins and indents, and set the box-sizing on border-box to make the application look the same in different browsers. Finally, we set the body background to a dark tone, which helps to highlight the application's content.

Loading media

We will need a couple of avatars to represent the authors of messages from the user and the OpenAI API. This way they will be easier to distinguish between them. Create a new icons folder inside src catalog and include bot.png и user.png Icons. You can download samples from the icon catalog here, or use your own from sites like FlatIcon or Icons8, as long as you keep the above file names.

Component creation

First, we need a well-organized file structure that matches the design of the framework. Using the terminal, we will create the necessary folders and component files. Each component will have its own JavaScript file for functionality and CSS file for styling. Change the working directory in the folder src by starting a folder cd src and then run the following command:
 mkdir components && cd components && touch Message.js Message.module.css Input.js Input.module.css History.js History.module.css Clear.js Clear.module.css 
The above command will first create /components/ folder, then change the working directory to it and create all the necessary files in it.

Component "Message

Website Message The component will display user prompts and API responses in the conversation, facilitating real-time communication between the user and the chatbot. Open Message.js. file and include the following code:
 import bot from "../icons/bot.png" ; import user from "../icons/user.png" ; import styles from "./Message.module.css" ; export default function Message ( { role , content } ) { return ( < div className = { styles . wrapper } > < div > < div > < img src = { role === "assistant" ? bot : user } className = { styles . avatar } alt = "profile avatar" / > < / div > < div > < div > < div > < div > < p > < p > { content } < / p > < / div > < / div > < / div > ) ; } 
First we import the uploaded icons for avatars, and then import external CSS rules for styling. After that, we create a wrapper for Message A component that will contain both icons and text content. We're using part parameter in the condition to display the corresponding avatar as an image src . We also use content which will be passed as a text response from the OpenAI API and a prompt for user input. Now let's design the component to look like a chat message! Open the component Message.module.css file and include the following rules:
 .wrapper { display : grid ; grid-template-columns : 60 px auto ; grid-template-columns : 60 px auto ; min-height : 60 px ; padding : 20 px ; margin-bottom : 20 px ; border-radius : 10 px ; background-color : #1b1b1d ; } .avatar { width : 40 px ; height : 40 px ; } 
We'll split the layout into two columns, with avatars displayed in a fixed-width container on the right and text on the left. We'll then add padding and margins to the bottom of the post. We'll also give the message round borders and set a dark gray background. Finally, we'll set the avatar icon to a fixed width and height.

Component "Input

Website Entry component will be an interface element designed to collect user requests and serve as the means by which users interact with the chatbot. Open Input.js. file and include the following code:
 import styles from "./Input.module.css" ; export default function Input ( { value , onChange , onClick } ) { return ( < div className = { styles . wrapper } > < input className = { styles . text } placeholder = "Your request here..." value = { value } onChange = { onChange } / > < button className = { styles . btn } onClick = { onClick } > Go < / button > < / div > ) ; } 
First, we import an external stylesheet to design the component. We return a component wrapper that includes an input field for custom prompts and a button to submit to the API. We set a placeholder value to be displayed when the input form is empty, and create a file significance a requisite for storing the entered hint, and also onChange which will be called when the input value is changed. For the button onClick prop will be called as soon as the user clicks the button. Now let's style the component so that the input area looks beautiful and the user is encouraged by the prompts! Open the component Input.module.css file and include the following rules:
 .wrapper { display : grid ; grid-template-columns : auto 100 px ; height : 60 px ; border-radius : 10 px ; background-color : #323236 ; } .text { border : none ; outline : none ; background : none ; padding : 20 px ; color : white ; font-size : 16 px ; } .btn { border : none ; border-radius : 0 10 px 10 px 0 ; font-size : 16 px ; font-weight : bold ; background-color : rgb ( 218 , 255 , 170 ) ; } .btn :hover { cursor : pointer ; background-color : rgb ( 200 , 253 , 130 ) ; } 
We set the wrapper to be divided into two columns, with a fixed width for the button and the rest of the available width dedicated to the input area. We also set a specific height for the component, set rounded borders for it, and set a dark gray background. For the input area, we'll remove the default borders, outline, and background and add some background. We'll set the text color to white and set a specific font size.

History component

Website History The component displays a sequence of past interactions between the user and the chatbot, providing users with contextual help about their conversation. Open History.js. file and include the following code:
 import styles from "./History.module.css" ; export default function History ( { question , onClick } ) { return ( < div className = { styles . wrapper } onClick = { onClick } > < p > { question . substring ( 0 , 15 ) } ... < / p > < / div > ) ; } 
First we import the external style rules for the component. Then we return a wrapper that will include text. The value of the text will be passed as issue prop from the user prompt, and only the first 15 characters of the text string will be displayed. Users will be allowed to click on history items, and we will pass the parameter onClick to control the click behavior. Now let's style the component so that it is visually appealing and fits well in the sidebar! Open History.module.css file and include the following rules:
 .wrapper { padding : 20 px ; margin-bottom : 20 px ; border-radius : 10 px ; background-color : #1b1b1d ; } .wrapper :hover { cursor : pointer ; background-color : #323236 ; } 
We'll set the backing, add a bottom indent, and set rounded corners for the story elements. We'll also set the background color to dark gray. When the user hovers over the element, it will turn into a pointer and the background color will change to a lighter shade of gray.

Clear component

Website Clear The component will be a UI element designed to reset or clear the current conversation, giving users a quick way to start a new interaction without disconnecting from the current interface. Open Clear.js. file and include the following code:
 import styles from "./Clear.module.css" ; export default function Clear ( { onClick } ) { return ( < button className = { styles . wrapper } onClick = { onClick } > Clear < / button > ) ; } ; } 
First, we import an external stylesheet to style the component. We return a button that will allow users to clear the contents of the application. We pass onClick to achieve the desired behavior. Now let's style the component to make it stand out and reduce the likelihood of accidental clicks! Open the component Clear.module.css file and include the following rules:
 .wrapper { width : 100% ; height : 60 px ; background-color : #ff9d84 ; border : none ; border-radius : 10 px ; font-size : 16 px ; font-weight : bold ; } .wrapper :hover { cursor : pointer ; background-color : #ff886b ; } 
We'll set the button so that it fills the entire available column width, set a specific height, and set a soft red background color. We will also remove the default borders, set rounded corners, set a specific font size and make it bold. On hover, the cursor will change to a pointer and the background color will change to a darker shade of red.

Creating a user interface

In the previous section, we created all the necessary components. Now let's put them together and create the application's user interface. We will customize their functionality to create a functional and interactive chatbot interface with streamlined and reusable code. Open App.js. file and include the following code:
 import { useState } from "react" ; import Message from "./components/Message" ; import Input from "./components/Input" ; import History from "./components/History" ; import Clear from "./components/Clear" ; import "./styles.css" ; export default function App ( ) { const [ input , setInput ] = useState ( "" ) ; const [ messages , setMessages ] = useState ( [ ] ) ; const [ history , setHistory ] = useState ( [ ] ) ; return ( < div className = "App" > < div className = "Column" > < h3 className = "Title" > Chat Messages < / h3 > < div className = "Content" > { messages . map ( ( ( el , i ) => { return < Message key = { i } role = { el . role } content = { el . content } / > > ; } ) } < / div > < Input value = { input } onChange = { ( e ) => setInput ( e . target . value ) } onClick = { input ? handleSubmit : undefined } / > < / div > < div className = "Column" > < h3 className = "Header" > History < / h3 > < div className = "Contents" > { history . map ( ( el , i ) => { return ( < History key = { i } question = { el . question } onClick = { ( ) => setMessages ( [ { role : "user" , content : history [ i ] . question } , { role : "assistant" , content : history [ i ] . answer } , ] ) } / > ) ; } ) } < / div > < Clear onClick = { clear } / > < / div > < / div > < / div > ) ; } 
First we import useState hook, which we will use to track the state of the data in the application. We then import all the components we created and the external stylesheet for styling. Then we create inlet A state variable to store the user's request input, messages to store the conversation between the user and ChatGPT, and history to store the history of user prompts. We are also creating a main wrapper for the entire application that will contain two columns. Each column will have a header and a content wrapper that will include conversation messages, an input area, and a Send for the first column and history elements and Clear For the second column. Conversation messages will be generated by matching via messages state variable and history elements - by matching through the history state variable. We set the input onChange update requisite inlet state variable every time the user enters any value into the input form. As soon as the user presses the Send button, the user's request will be sent to the OpenAI API for processing and receiving a response. For history elements we set the value onClick props for messages the state variable is updated to the specific request and response. Finally, for Clear we pass the button onClick Write a function that will clear the message and history values by clearing the application data.

Creating an application layout

In this section, we will arrange the user interface components to create an intuitive structure for effective user interaction. Open App.css. and include the following styling rules:
 .App { display : grid ; grid-template-columns : auto 200 px ; gap : 20 px ; max-width : 1000 px ; margin : 0 auto ; min-height : 100 vh ; padding : 20 px ; } .Column { color : white ; } .Title { padding : 20 px ; margin-bottom : 20 px ; border-radius : 10 px ; color : black ; background-color : rgb ( 218, 255, 170 ) ; } .Content { height : calc ( 100 vh - 200 px ) ; overflow-y : scroll ; margin-bottom : 20 px ; } ::-webkit-scrollbar { display : none ; } 
We divided the main app wrapper into two columns separated by a gap using CSS grid layout , and set a fixed width for the left column for the story elements. We then positioned the wrapper so that it never exceeded a certain width, centered it on the screen, made it use the full height of the screen viewing area, and added a backing within it. For the contents of each column, we set the text color to white. For the column headers, we'll set the padding, add a bottom margin, and set rounded corners. We'll also set the background color of the header element to lime green and the text color to black. We'll also design the columns themselves, setting a rule that the content should not exceed a certain height, and set the content to scroll if it goes beyond the height. We also add an indentation from the bottom border. We also hide the scrollbars so we don't have to change their style to override the defaults for each browser. This rule is optional and we can choose not to follow it.

Obtaining an API key from OpenAI

If you have not already configured your own sandbox API key in the introduction to this guide, be sure to create an account on the OpenAI website. Then log in, go to the API keys section, and generate a new API key.
setting up an api key
Copy the key to the clipboard and open your project. Create a new.env file in the root of the project and insert a value for the following key as shown below:
 REACT_APP_OPENAI_API_KEY = paste-your-code-here 

Preparing a request call to the OpenAI API

Using the OpenAI API, our chatbot will be able to send text requests to the OpenAI server, which will process them and generate human-like responses. This is accomplished by utilizing a powerful language model that has been trained on a variety of text sources. By providing the model with the conversation history and current user prompt, our chatbot will receive context-aware responses from the API. In this section, we will prepare the request and implement the API call to receive the response and set the data into the state variable we defined earlier. Open App.js. and add the following code:
 // imported modules ... export default function App ( ) { // useState variables ... const handleSubmit = async ( ) => { const prompt = { role : "user" , content : input, } ; setMessages ( [ ... messages , prompt ] ) ; await fetch ( "https://api.openai.com/v1/chat/completions" , { method : "POST" , headers : { Authorization : ` Bearer ${ process . env . REACT_APP_OPENAI_API_API_KEY } ` , "Content-Type" : "application/json", } , body : JSON . stringify ( { model : "gpt-3.5-turbo" , messages : [ ... messages, prompt ], } ), } ) . then ( ( ( data ) => data . json ( ) ) . then ( data ) => { const res = data . choices [ 0 ] . message . content ; setMessages ( ( messages ) => [ ... messages , { role : "helper", content : res, }, ] ) ; setHistory ( history ) => [ ... history , { question : input, answer : res } ] ) ; setInput ( "" ) ; } ) ; } ; const clear = ( ) => { setMessages ( [ ] ) ; setHistory ( [ ] ) ; } } ; return < div className = "App" > // return elements ... ; } 
First, we'll create a separate handleSubmit function that will be executed after the user enters a query into the input form and clicks the button Send Button. Inside handleSubmit First, we're going to create give me a hint variable that will play the role of user and the message itself as an object. The role is important because when storing messages we need to know which ones are user messages. Then we update messages state variable with a user prompt. Next, we create the actual get calling the api.openai.com/v1/chat/completions endpoint to access data from the OpenAI API. We specify that this is POST request and set the authorization token and content type in the headers. For body parameters, we specify which API model to use, and pass the messages variable as content from the user. After receiving the response, we store it in the variable res variable. We add an object consisting of a role assistant and the very answer to message state variable. We're also updating history state variable with the object, s issue and corresponding response as keys. After receiving the response and updating the state variables, we clear the inlet state variable to prepare the input form for the next user request. Finally, we create a simple variable clear to clean up messages и history state variables, allowing the user to clear the application data.

Application testing

By now we should have created a fully functional chat app! The only thing left to do is to test it. First, let's try to ask ChatGPT one question.
A question asked via our new app
The animation above shows the question asked and the answer received. Now let's try to create a conversation.
Submitting multiple questions
As shown in the animation above, the chatbot remembers the context of previous messages, so we can talk to it based entirely on context. Now let's see what happens if we click on the button History button.
Clicking on the History button
Notice how the chat switches to the appropriate user question and answer. This can come in handy if we want to resume the conversation from a certain point. Finally, let's click the button Clear button.
Clicking on the Clear button
As expected, the contents of the application will be cleaned up. This is a useful option if there is a lot of content and the user wants to start from scratch.

Conclusion

In this tutorial, we learned how to create a user-friendly UI, how to structure code with components, how to work with states, how to make API calls, and how to process the resulting data. With the combination of the advanced natural language processing capabilities of the OpenIAI API and the flexibility of React, you will now be able to create complex chatbot applications that can be tweaked to your liking. Note that this guide stores the API key on the frontend, which may not be safe for production. If you want to deploy the project, it is recommended that you create an Express server and use the API key there. Also, if you want the history prompts to be available after the next run, you can store and then read them from local storage or even connect a database to your application and store and read the data from there.

Frequently Asked Questions (FAQ) about creating a ChatGPT clone using React and OpenAI APIs

What is the main purpose of using React to create a ChatGPT clone?

React is a popular JavaScript library for creating user interfaces, especially for single-page applications. It is used to handle the presentation layer in web and mobile applications. React allows you to create simple views for each application state and efficiently update and display the right components when data changes. In the context of creating a ChatGPT clone, React provides a flexible and efficient way to structure the chat interface, manage state changes such as incoming and outgoing messages, and handle user interaction.

How does the OpenAI API affect the functionality of the ChatGPT clone?

The OpenAI API is the core engine that provides the conversational capabilities of the ChatGPT clone. It provides access to the GPT-3 model, a state-of-the-art machine learning model for natural language processing tasks. When you send user input to the API, it returns a generated message that mimics a human-like response. This interaction is at the heart of the chat functionality in the ChatGPT clone.

Can I customize the ChatGPT clone to my needs?

Absolutely. The beauty of creating your own ChatGPT clone is the ability to customize it to your needs. You can change the user interface, add additional features, or even integrate it with other services or APIs. The provided code serves as a starting point, and you can experiment and do things your own way.

What are the prerequisites for creating a ChatGPT clone?

To create a ChatGPT clone, you need a basic understanding of JavaScript and React. You also need a development environment with Node.js and npm (Node Package Manager). In addition, you need access to the OpenAI API, which requires an API key that can be obtained by registering on the OpenAI website.

How can I deal with OpenAI API errors or unexpected responses?

The OpenAI API may periodically return errors or unexpected responses for various reasons, such as network problems, invalid requests, or API limitations. You can handle these situations by implementing error handling logic in your code. This may include retrying the request, showing an error message to the user, or logging the error for further investigation.

How can I improve the performance of my ChatGPT clone?

There are several ways to optimize the performance of your ChatGPT clone. One is to implement efficient state management in React components to minimize unnecessary rendering. Another is to leverage the OpenAI API, for example, by limiting the frequency of requests or reducing the amount of data sent in each request.

Is it possible to deploy a ChatGPT clone on a server?

Yes, you can deploy your ChatGPT clone on a server to make it available on the Internet. To do this, you need to package the application in a format suitable for deployment, create a server environment, and configure the server to serve the application. There are many hosting providers that can simplify this process, such as Vercel, Netlify, or Heroku.

Can I use other machine learning models with the ChatGPT clone?

Although the provided code is designed to work with the OpenAI API, you can modify it to use other machine learning models if you wish. To do this, you need to replace API calls with API calls of the model you have chosen and, possibly, adjust the logic of data processing according to the new model.

How can I add more features to the ChatGPT clone?

Adding additional features to a ChatGPT clone involves writing additional code and integrating it with the existing code base. Some potential features may include support for multiple chats, user authentication, message history, or even multimedia messaging. The possibilities are limited only by your imagination and coding skills.

How do I secure the communication between the ChatGPT clone and the OpenAI API?

Securing the communication between your ChatGPT clone and the OpenAI API is very important to protect your API key and the data being transmitted. This can be achieved by using HTTPS for all requests to the API, which encrypts the data in transit. In addition, you must store your API key securely and not disclose it in client-side code.

Let's get in touch!

Please feel free to send us a message through the contact form.

Drop us a line at mailrequest@nosota.com / Give us a call over skypenosota.skype