Build an AI Chat Assistant in React
Build an AI Chat Assistant in React
Full‑Stack AI: React + Express + OpenAI
By AI Learning Assistant · React · Express · OpenAI API · Chat History
🤖 YOUR KIND AI LEARNING ASSISTANT
Welcome to Day 25! Today we're building something truly exciting — your own AI chat assistant, powered by OpenAI. You'll connect a React frontend to a Node.js backend, send messages, and get intelligent responses. I'll explain every step, from CORS to conversation history. Don't worry — we'll go slowly. You've got this! 🚀
What We Are Building
A FULL‑STACK AI CHAT APPLICATION
We'll create a chat interface where you can type a question about JavaScript or React, and an AI assistant (powered by OpenAI's GPT) will answer. The app has two parts:
- Backend (Express server) — Holds your OpenAI API key securely, receives messages from React, calls the OpenAI API, and sends back the AI's reply.
- Frontend (React app) — Displays the chat UI, sends user messages to the backend, shows typing indicators, and displays the AI's responses.
🎯 WHY TWO SERVERS? (REACT + EXPRESS)
Your API key must stay secret — never put it in React code (anyone can see it!). So we put it in a Node.js backend. The React app (running on port 3000) talks to the backend (port 5000). The backend talks to OpenAI. This is a standard full‑stack pattern.
Step 1 — Building the Backend (server.js)
EXPRESS · CORS · OPENAI SDK · ENVIRONMENT VARIABLES
First, create a new folder for your backend (e.g., `webbo3-ai-backend`). Inside, initialize npm and install dependencies:
npm init -y npm install express cors openai dotenv
📜 server.js — Complete Backend Code (with line‑by‑line comments)
// ============================================ // AI Chat Backend (Express + OpenAI) // ============================================ // Load environment variables from .env file (keeps API key secret) require("dotenv").config(); // Import Express (web framework for Node.js) const express = require("express"); // CORS allows your React app (port 3000) to talk to this server (port 5000) // Without CORS, browsers block requests from different origins. const cors = require("cors"); // OpenAI SDK — official library to call GPT models const OpenAI = require("openai"); // Create the Express app const app = express(); // Enable CORS for all routes (simplifies development) app.use(cors()); // Enable JSON body parsing — so we can read req.body.message app.use(express.json()); // Initialize OpenAI client with API key from environment variables const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); // ============================================ // POST /chat — receives a user message and returns AI reply // ============================================ app.post("/chat", async (req, res) => { // Extract the message sent from React frontend const { message } = req.body; // Call OpenAI's chat completion API const response = await client.chat.completions.create({ model: "gpt-3.5-turbo", messages: [ { // System prompt — gives the AI a personality and role role: "system", content: "You are Webbo3's AI tutor. Help students learn JavaScript and React. Be encouraging and keep answers short (1‑2 paragraphs)." }, { role: "user", content: message } ] }); // Extract the reply text from the response const reply = response.choices[0].message.content; // Send the reply back to React as JSON res.json({ reply }); }); // Start the server on port 5000 app.listen(5000, () => { console.log("AI server running on http://localhost:5000"); });
📌 IMPORTANT: CREATE .env FILE
In your backend folder, create a file named .env with your OpenAI API key:
OPENAI_API_KEY=sk‑your‑actual‑key‑here
Then add .env to your .gitignore to keep it secret!
Step 2 — Building the Frontend (React App)
CHAT UI · STATE MANAGEMENT · FETCH TO BACKEND
Now let's create the React chat interface. Create a new React app (or use your existing one) and replace src/App.js with the code below.
📜 src/App.js — Complete React Chat Component
// ============================================ // AI Chat Assistant — React Frontend // ============================================ import { useState } from "react"; function App() { // messages array stores all conversation turns: user messages and assistant replies const [messages, setMessages] = useState([ { role: "assistant", text: "Hi! I am Webbo3 AI. Ask me anything about JavaScript!" } ]); // Current input value (what user is typing) const [input, setInput] = useState(""); // Loading state — true while waiting for AI response, shows "thinking..." const [loading, setLoading] = useState(false); // Called when user clicks Send or presses Enter const sendMessage = async () => { // Don't send empty messages if (!input.trim()) return; // Save the user's message text before clearing input const userMessage = input; // Add user message to conversation history setMessages(prev => [...prev, { role: "user", text: userMessage }]); // Clear input field setInput(""); // Show loading indicator setLoading(true); // Send the user message to our backend (port 5000) const res = await fetch("http://localhost:5000/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: userMessage }) }); // Parse the JSON response from backend const data = await res.json(); // Add assistant's reply to conversation history setMessages(prev => [...prev, { role: "assistant", text: data.reply }]); // Hide loading indicator setLoading(false); }; // Render the chat UI return ( <div style={{ padding: "40px", maxWidth: "600px", margin: "0 auto" }}> <h1>Webbo3 AI Tutorh1> // Chat message container (scrollable) <div style={{ border: "1px solid #ccc", borderRadius: "8px", padding: "20px", height: "400px", overflowY: "scroll", marginBottom: "10px" }}> {messages.map((msg, i) => ( <div key={i} style={{ textAlign: msg.role === "user" ? "right" : "left", margin: "8px 0" }}> <span style={{ background: msg.role === "user" ? "#007bff" : "#f0a500", color: "white", padding: "8px 14px", borderRadius: "20px", display: "inline-block" }}> {msg.text} span> div> ))} {loading && <p>Webbo3 AI is thinking...p>} div> // Input area and send button <input value={input} onChange={e => setInput(e.target.value)} onKeyPress={e => e.key === "Enter" && sendMessage()} placeholder="Ask about JavaScript..." style={{ width: "80%", padding: "10px" }} /> <button onClick={sendMessage} style={{ padding: "10px 20px", background: "#007bff", color: "white", border: "none" }}>Sendbutton> div> ); } export default App;
🎯 Live Simulation — Try the Chat Interface (Demo)
✨ This simulates the UI (no real AI). In your real app, it will connect to OpenAI.
How to Run the Complete App
TERMINAL · TWO SERVERS · TEST
# 1. BACKEND (in webbo3-ai-backend folder) node server.js # You should see: AI server running on port 5000 # 2. FRONTEND (in your React app folder, e.g., webbo3-react) npm start # React app opens on http://localhost:3000 # 3. Open your browser to http://localhost:3000 # Type a message and watch the AI respond!
Quick Reference — Full‑Stack AI Chat
| Concept | What it does |
|---|---|
| CORS | Allows React app on port 3000 to call backend on port 5000 |
| dotenv | Loads API key from .env file (keeps secrets out of code) |
| System prompt | Gives the AI a personality and rules (e.g., "be encouraging") |
| Conversation history | Array of messages stored in React state |
| Threading | Sending full history to maintain context (future improvement) |
🤗 YOUR KIND AI ASSISTANT — FINAL TIPS
💬 "My fetch is failing (CORS error)" → Make sure your backend is running on port 5000 and you have `app.use(cors())`.
💬 "How do I add typing animation?" → You can simulate typing by streaming the response or adding a delayed character‑by‑character effect.
💬 "What about pricing?" → GPT‑3.5‑turbo is very cheap (fractions of a cent per message). Set a spending limit in your OpenAI account.
You did it! You built a full‑stack AI chat app — from React frontend to Express backend to OpenAI. This is a portfolio‑worthy project. Be proud of yourself. See you tomorrow! 🌟
AI-Assisted JavaScript Learning · Build an AI Chat Assistant in React · Full‑Stack with OpenAI
Complete this lesson
Mark as complete to track your progress