How to Add a Custom ChatGPT Widget to Your React Site
Ever landed on a site that could answer your questions in real time, and thought “I could build that”? In 2024 AI assistants are no longer a novelty – they’re a user expectation. Adding a ChatGPT widget to a React app lets you give visitors instant help, collect feedback, or just show off a cool tech trick. Below is a step‑by‑step guide that walks you through the whole process, from setting up the API key to polishing the UI. Grab a coffee, fire up your editor, and let’s get coding.
Why put a ChatGPT widget on your site?
A chat widget does three things for a typical web project:
- Improves user experience – visitors get answers without leaving the page.
- Reduces support load – simple questions are handled automatically.
- Shows you’re up‑to‑date – a live AI feels modern and can be a talking point in interviews.
I added a widget to a personal portfolio last month, and the bounce rate dropped by a few percent. Not a huge metric, but it felt good to see people actually typing “What tech stack did you use?” and getting a quick reply.
What you need before you start
- A React project (created with
create‑react‑app, Vite, or Next.js). - An OpenAI API key – you can get one from the OpenAI dashboard.
- Basic knowledge of JavaScript and React hooks.
- A tiny bit of CSS sense for styling the chat bubble.
If any of these sound unfamiliar, pause and check the official docs. The steps below assume you already have a running React app on localhost:3000.
Install the required packages
Open a terminal in your project folder and run:
npm install openai axios
- openai – the official client library that talks to the ChatGPT model.
- axios – a simple HTTP client we’ll use to send requests from the browser.
Both packages are lightweight and have good TypeScript support, but you can skip the types if you’re writing plain JavaScript.
Create the Chat widget component
Create a new file called ChatWidget.jsx (or .tsx if you prefer TypeScript) inside src/components. Here’s a minimal version:
import React, { useState } from "react";
import axios from "axios";
export default function ChatWidget() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const sendMessage = async () => {
if (!input.trim()) return;
const userMsg = { role: "user", content: input };
const newMessages = [...messages, userMsg];
setMessages(newMessages);
setInput("");
setLoading(true);
try {
const response = await axios.post("/api/chat", {
messages: newMessages,
});
const botMsg = response.data;
setMessages([...newMessages, botMsg]);
} catch (err) {
console.error(err);
// Show a simple error message
setMessages([...newMessages, { role: "assistant", content: "Oops, something went wrong." }]);
} finally {
setLoading(false);
}
};
return (
<div className="chat-widget">
<div className="chat-history">
{messages.map((msg, i) => (
<div key={i} className={msg.role}>
{msg.content}
</div>
))}
{loading && <div className="assistant">Thinking...</div>}
</div>
<div className="chat-input">
<input
type="text"
value={input}
placeholder="Ask me anything..."
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && sendMessage()}
/>
<button onClick={sendMessage} disabled={loading}>
Send
</button>
</div>
</div>
);
}
A quick rundown:
- messages stores the conversation as an array of objects. Each object has a
role(userorassistant) andcontent(the text). - sendMessage adds the user’s text to the array, then calls a backend route (
/api/chat) that will forward the request to OpenAI. - The UI shows a simple list of messages and a loading indicator while the model thinks.
Feel free to tweak the class names and add your own CSS. I like a rounded bubble for the user and a lighter one for the assistant.
Set up a serverless API route
Because the OpenAI key must stay secret, you can’t call the API directly from the browser. In a Create‑React‑App project you can add a tiny Express server, but if you’re using Vite or Next.js the easiest path is a serverless function.
Create a folder called api at the project root and add chat.js:
const { Configuration, OpenAIApi } = require("openai");
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
module.exports = async (req, res) => {
if (req.method !== "POST") {
res.status(405).send("Method Not Allowed");
return;
}
const { messages } = req.body;
try {
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages,
});
const reply = completion.data.choices[0].message;
res.json(reply);
} catch (error) {
console.error(error);
res.status(500).json({ role: "assistant", content: "Error contacting AI." });
}
};
What’s happening here?
- Configuration holds the API key, which you should store in an
.envfile asOPENAI_API_KEY. - createChatCompletion sends the whole conversation so the model can keep context.
- The response is a single message object that we forward back to the front‑end.
If you’re on Vite, you can use vite-plugin-node or set up a tiny Express server that runs alongside npm run dev. The key point is: never expose the API key in client code.
Add the widget to a page
Now import the component where you want it to appear, for example in src/App.jsx:
import ChatWidget from "./components/ChatWidget";
function App() {
return (
<div className="App">
<h1>Welcome to My Site</h1>
{/* other content */}
<ChatWidget />
</div>
);
}
export default App;
That’s it – the widget should pop up at the bottom of the page. If you prefer a floating button that opens a modal, wrap the component in a small state toggle and use CSS position: fixed.
Test, tweak, and ship
- Run the dev server (
npm startorvite). Open the site and type a question like “What is React?” – you should see a response within a second or two. - Check error handling – temporarily remove the API key and see that the “Oops, something went wrong.” message appears.
- Style for mobile – make the input field full width on small screens and ensure the chat bubbles don’t overflow.
- Limit token usage – the model charges per token, so you may want to cap the conversation length. A simple way is to keep only the last 10 messages before sending the request.
Once you’re happy, push the code to your repo and deploy. Services like Vercel or Netlify will automatically pick up the serverless function, as long as you add the OPENAI_API_KEY environment variable in their dashboard.
A quick personal note
When I first tried to embed ChatGPT, I kept getting “Rate limit exceeded” errors because I was sending the whole conversation every time. The fix was to slice the array to the most recent 6 messages – enough for context, light on tokens, and it made the UI feel snappier. Small tweaks like that turn a good demo into a production‑ready feature.
Enjoy building your own AI chat. It’s a neat way to learn about APIs, React hooks, and the power of conversational models – all in a single, useful widget.
- → How to Choose the Right AI Productivity Tool for Your Remote Team @remoteaitoolbox
- → How to Build a Secure AI-Powered Chatbot on AWS Free Tier: A Step-by-Step Guide @techinsighthub
- → A Step-by-Step Guide to Deploying a Custom AI Model on a Budget @techinsightlab
- → Choosing the Right Memory Architecture for AI Workloads: Practical Criteria and Benchmarks @memorymatters
- → How to Build a Custom AI Accelerator on a Breadboard @futurecircuit