DEV Community

Cover image for Building ChatGPT with React
Kacey Cleveland
Kacey Cleveland

Posted on

Building ChatGPT with React

Building out a ChatGPT clone using OpenAI's API is a great way to familiarize yourself both with OpenAI and React in general in itself. This post will go over a high level example of implementing a ChatGPT clone and the implementation I used in my side project. My codebase is very much a work in progress but you can follow along with my progress in my side project below!

https://github.com/kaceycleveland/help-me-out-here

Note: This is calling the OpenAI API on the client; it should not be used as is unless the requests are being proxied through a backend service of some sort to hide your API keys or if you are truly building a client side application.

Image description

Dependencies

To send and receive messages to OpenAI we can use OpenAI's official npm package:

https://www.npmjs.com/package/openai

In addition to this, we will be using TypeScript and Tanstack Query. Tanstack Query will serve as a wrapper to help send and process data to be consumed by our react application. You can read more about Tanstack Query here.

1. Instantiate the OpenAI Client

We first need a way to send OpenAI chat completion requests and get the responses back using the OpenAI npm package:

import { Configuration, OpenAIApi } from "openai"; const createOpenAiClient = () => { const config = new Configuration({ organization: import.meta.env.OPENAI_ORG, apiKey: import.meta.env.OPENAI_KEY, }); return new OpenAIApi(config); }; export const openAiClient = createOpenAiClient(); 
Enter fullscreen mode Exit fullscreen mode

Now we can use the openAiClient to create chat completion requests as described here.

  1. Create a Chat Mutation Hook

We can now create a react hook wrapped around the OpenAI client to make calls to the OpenAI API.

import { useMutation, UseMutationOptions } from "@tanstack/react-query"; import { openAiClient } from "../openai"; import { CreateChatCompletionResponse, CreateChatCompletionRequest } from "openai"; import { AxiosResponse } from "axios"; export const useChatMutation = ( options?: UseMutationOptions< AxiosResponse<CreateChatCompletionResponse>, unknown, CreateChatCompletionRequest > ) => { return useMutation< AxiosResponse<CreateChatCompletionResponse>, unknown, CreateChatCompletionRequest >({ mutationFn: (request) => { return openAiClient.createChatCompletion(request) }, ...options, }); }; 
Enter fullscreen mode Exit fullscreen mode

3. Consume the useChatMutation hook

import { ChatCompletionRequestMessage } from "openai/dist/api"; import { useState, useCallback, useRef } from "react"; import { useChatMutation } from "./useChatMutation"; function App() { // Store the recieved messages and use them to continue the conversation with the OpenAI Client const [messages, setMessages] = useState<ChatCompletionRequestMessage[]>([]); const inputRef = useRef<HTMLTextAreaElement>(null); /** * Use the chat mutation hook to submit the request to OpenAI * This is a basic example, but using tanstack query lets you easily * render loading, error, and success states. * */ const { mutateAsync: submitChat } = useChatMutation({ onSuccess: (response) => { const foundMessage = response.data.choices.length ? response.data.choices[0].message : undefined; if (foundMessage) { const messageBody: ChatCompletionRequestMessage[] = [ ...messages, foundMessage, ]; setMessages(messageBody); } }, }); const handleSubmit = useCallback(() => { if (inputRef.current?.value) { const messageBody: ChatCompletionRequestMessage[] = [ ...messages, { role: "user", content: inputRef.current?.value }, ]; setMessages(messageBody); // For simplicility, the settings sent to OpenAI are hard coded here. submitChat({ model: "gpt-3.5-turbo", max_tokens: 100, presence_penalty: 1, frequency_penalty: 1, messages: messageBody, }); } }, [messages]); return ( <div className="App"> <div> {messages.map((message) => { return ( <div> <div>{message.role}</div> <div>{message.content}</div> </div> ); })} </div> <div className="card"> <textarea ref={inputRef}></textarea> <button onClick={handleSubmit}>Submit</button> </div> </div> ); } export default App; 
Enter fullscreen mode Exit fullscreen mode

Fin

This example can be expanded upon in various ways such as:

  • Customizing the interface/settings being sent with the messages
  • Customizing old and future messages to "prime" the AI for future responses.
  • Better UI rendering for different states
  • Better UI rendering for returned data. (Think rendering code blocks or markdown from the returned OpenAI data!)

Most of the above is what I am working on in my project:
https://github.com/kaceycleveland/help-me-out-here

If you want the full repo of this basic example, check it out here:
https://github.com/kaceycleveland/openai-example

Top comments (0)