Skip to content

💬 Build Your Chat Interface

Now let’s connect your frontend to your backend and create a simple chat interface. You’ll build everything in one component to keep it simple and focused on the AI functionality.


Replace the contents of your src/App.jsx with this complete chat interface:

import { useState } from 'react'
import { Send, Bot, User } from 'lucide-react'
function App() {
const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
const [loading, setLoading] = useState(false)
const sendMessage = async () => {
if (!input.trim()) return
const userMessage = { text: input, isUser: true }
setMessages(prev => [...prev, userMessage])
setInput('')
setLoading(true)
try {
const response = await fetch('http://localhost:8000/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: input }),
})
const data = await response.json()
if (data.success) {
const aiMessage = { text: data.response, isUser: false }
setMessages(prev => [...prev, aiMessage])
} else {
const errorMessage = { text: 'Sorry, something went wrong.', isUser: false }
setMessages(prev => [...prev, errorMessage])
}
} catch (error) {
console.error('Error:', error)
const errorMessage = { text: 'Failed to connect to server.', isUser: false }
setMessages(prev => [...prev, errorMessage])
} finally {
setLoading(false)
}
}
const handleKeyPress = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
<div className="bg-white rounded-lg shadow-lg w-full max-w-2xl h-[600px] flex flex-col">
{/* Header */}
<div className="bg-blue-500 text-white p-4 rounded-t-lg">
<h1 className="text-xl font-bold">AI Chat Assistant</h1>
<p className="text-blue-100">Ask me anything!</p>
</div>
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.length === 0 && (
<div className="text-center text-gray-500 mt-20">
<Bot className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<p>Start a conversation with your AI assistant!</p>
</div>
)}
{messages.map((message, index) => (
<div
key={index}
className={`flex items-start space-x-3 ${
message.isUser ? 'justify-end' : 'justify-start'
}`}
>
{!message.isUser && (
<div className="bg-blue-500 p-2 rounded-full">
<Bot className="w-4 h-4 text-white" />
</div>
)}
<div
className={`max-w-xs lg:max-w-md px-4 py-2 rounded-lg ${
message.isUser
? 'bg-blue-500 text-white'
: 'bg-gray-200 text-gray-800'
}`}
>
{message.text}
</div>
{message.isUser && (
<div className="bg-gray-500 p-2 rounded-full">
<User className="w-4 h-4 text-white" />
</div>
)}
</div>
))}
{loading && (
<div className="flex items-start space-x-3">
<div className="bg-blue-500 p-2 rounded-full">
<Bot className="w-4 h-4 text-white" />
</div>
<div className="bg-gray-200 text-gray-800 px-4 py-2 rounded-lg">
<div className="flex space-x-1">
<div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce"></div>
<div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style={{animationDelay: '0.1s'}}></div>
<div className="w-2 h-2 bg-gray-500 rounded-full animate-bounce" style={{animationDelay: '0.2s'}}></div>
</div>
</div>
</div>
)}
</div>
{/* Input */}
<div className="border-t p-4">
<div className="flex space-x-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type your message..."
className="flex-1 border border-gray-300 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={loading}
/>
<button
onClick={sendMessage}
disabled={loading || !input.trim()}
className="bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 text-white p-2 rounded-lg transition-colors"
>
<Send className="w-5 h-5" />
</button>
</div>
</div>
</div>
</div>
)
}
export default App

Let’s break down what this component does:

const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
const [loading, setLoading] = useState(false)

What each state does:

  • messages - Stores all chat messages (both user and AI)
  • input - Controls the text input field
  • loading - Shows when you’re waiting for AI response
const sendMessage = async () => {
// Add user message to chat
const userMessage = { text: input, isUser: true }
setMessages(prev => [...prev, userMessage])
// Call your backend API
const response = await fetch('http://localhost:8000/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: input }),
})
}

What this does:

  • Adds user message to the chat immediately
  • Sends message to your Node.js backend
  • Handles the AI response and adds it to chat
  • Message bubbles - Different styles for user vs AI messages
  • Loading animation - Bouncing dots while waiting for AI
  • Enter to send - Press Enter to send messages
  • Icons - Bot and User icons for visual clarity
  • Responsive design - Works on mobile and desktop

  1. Start your backend (in your node-backend folder):
Terminal window
npm run dev
  1. Start your frontend (in your chat-frontend folder):
Terminal window
npm run dev
  1. Open your browser to http://localhost:5173

  2. Try sending a message like:

    • “Hello, how are you?”
    • “Write a short poem about coding”
    • “Explain React in simple terms”

You should see your messages appear instantly, and AI responses come back from your backend!


IssueFix
CORS errors in browserMake sure your backend has cors() middleware
Network errorsCheck that backend is running on port 8000
Messages not sendingOpen browser dev tools to see error details
Styling looks wrongMake sure Tailwind CSS is properly installed
Loading state stuckCheck your backend console for errors

Congratulations! You’ve built a complete AI chat application that:

  • ✅ Connects React frontend to Node.js backend
  • ✅ Sends messages to OpenAI’s API
  • ✅ Shows real-time responses
  • ✅ Has a clean, simple UI
  • ✅ Handles errors gracefully

Your basic chat app is working! Next, you’ll add more advanced features like:

  • Message history persistence
  • Streaming responses for faster replies
  • File upload capabilities
  • Better error handling

You’ve built your first AI-powered application! 🎊