"Every developer says they want to build AI projects. Most spend weeks planning and never start. Here's how to go from zero to deployed AI chatbot in under 100 lines — today."
Let me guess your situation right now. 👇
You've seen ChatGPT. You've used Claude. You've thought — "I want to build something like this."
Then you Googled "how to build AI chatbot" and got hit with a wall of tutorials — LangChain, vector databases, embeddings, RAG pipelines, fine-tuning... 😵
And you closed the tab. Because it felt way too complicated.
Here's the truth nobody tells you. 🤫
A fully working, deployed, impressive AI chatbot takes under 100 lines of code. No LangChain. No vector databases. No PhD required.
This post is that chatbot. Complete. Working. Copy-paste ready.
By the end of this — you will have a deployed AI chatbot with a live URL. Not tomorrow. Today. ⏱️
Let's go. 👇
🗺️ What We're Building
Before we touch code — here's exactly what we're building: 🎯
✅ What the final chatbot has:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🤖 Custom AI personality (you define it)
💬 Full conversation memory (remembers context)
⚡ Streaming responses (words appear instantly)
📱 Mobile responsive UI
🌐 Deployed on Vercel with live URL
🔒 API key secured server-side
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total lines of code: ~95 🤯
Time to build: 60-90 minutes ⏱️
Cost: Free tier covers hundreds of chats 🆓
Let's build it step by step. 👇
🛠️ Step 1 — Project Setup (5 Minutes)
# Create Next.js app 🚀
npx create-next-app@latest ai-chatbot --typescript --tailwind --app
cd ai-chatbot
# Install OpenAI SDK 📦
npm install openai
# Create environment file 🔒
touch .env.local
# .env.local — add your OpenAI API key
OPENAI_API_KEY=sk-your-key-here
# Get free key at: platform.openai.com 🆓
# .gitignore — make sure this line exists! ⚠️
.env.local
That's setup. Done. 5 minutes. ✅
🛠️ Step 2 — The API Route (20 Lines) 🔥
This is the backend brain of your chatbot. Every message goes through here. 🧠
// app/api/chat/route.ts
// 20 lines. Full AI backend. 🤯
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY // 🔒 server-side only
})
export async function POST(req: Request) {
const { messages } = await req.json()
const stream = await openai.chat.completions.create({
model: 'gpt-4o-mini',
stream: true, // ⚡ words appear instantly
messages: [
{
role: 'system',
// 🎨 THIS is where you define your chatbot's personality!
content: `You are a helpful coding assistant specializing in
React and Next.js. You give concise, practical answers
with code examples. You're friendly and encouraging
to beginners. Never give up on a question.`
},
...messages // 💬 full conversation history
]
})
// Stream response back to frontend ✨
return new Response(stream.toReadableStream())
}
20 lines. Full streaming AI backend. That's it. 🎯
💡 The personality trick: Change that
systemprompt and you have a completely different chatbot. A fitness coach. A recipe sugganger. A sarcastic code reviewer. The system prompt IS the product. 🎨
🛠️ Step 3 — The Chat UI (75 Lines) 💬
Now the frontend. This is the part users see and interact with. 👇
// app/page.tsx
// 75 lines. Full chat UI with streaming. 🔥
'use client'
import { useState, useRef, useEffect } from 'react'
// Message type definition 📝
type Message = {
role: 'user' | 'assistant'
content: string
}
export default function ChatBot() {
const [messages, setMessages] = useState<Message[]>([])
const [input, setInput] = useState('')
const [loading, setLoading] = useState(false)
const bottomRef = useRef<HTMLDivElement>(null)
// Auto-scroll to latest message 📜
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
const sendMessage = async () => {
if (!input.trim() || loading) return
const userMessage: Message = { role: 'user', content: input }
const updatedMessages = [...messages, userMessage]
setMessages(updatedMessages)
setInput('')
setLoading(true)
// Add empty assistant message — we'll stream into it ⚡
setMessages(prev => [...prev, { role: 'assistant', content: '' }])
try {
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: updatedMessages })
})
// Read the stream chunk by chunk 🌊
const reader = res.body!.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
// Parse SSE chunks from OpenAI stream 📦
const lines = chunk.split('\n').filter(l => l.startsWith('data: '))
for (const line of lines) {
const data = line.replace('data: ', '')
if (data === '[DONE]') break
try {
const parsed = JSON.parse(data)
const text = parsed.choices[0]?.delta?.content || ''
// Append each word to the last message ✨
setMessages(prev => {
const updated = [...prev]
updated[updated.length - 1].content += text
return updated
})
} catch { /* skip malformed chunks */ }
}
}
} catch (err) {
setMessages(prev => {
const updated = [...prev]
updated[updated.length - 1].content = '❌ Something went wrong. Try again.'
return updated
})
} finally {
setLoading(false)
}
}
// Send on Enter key ⌨️
const handleKey = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
{/* Header 🤖 */}
<div className="text-center py-4 border-b">
<h1 className="text-2xl font-bold">🤖 AI Coding Assistant</h1>
<p className="text-gray-500 text-sm">Powered by GPT-4o-mini</p>
</div>
{/* Messages 💬 */}
<div className="flex-1 overflow-y-auto py-4 space-y-4">
{/* Empty state */}
{messages.length === 0 && (
<div className="text-center text-gray-400 mt-20">
<p className="text-4xl mb-3">💬</p>
<p className="text-lg">Ask me anything about React or Next.js!</p>
<p className="text-sm mt-2">I remember our whole conversation 🧠</p>
</div>
)}
{/* Message bubbles */}
{messages.map((msg, i) => (
<div
key={i}
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] px-4 py-3 rounded-2xl text-sm leading-relaxed
${msg.role === 'user'
? 'bg-blue-600 text-white rounded-br-none' // 👤 user — right
: 'bg-gray-100 text-gray-800 rounded-bl-none' // 🤖 AI — left
}`}
>
{/* Streaming cursor effect ▊ */}
{msg.content || (loading && i === messages.length - 1
? <span className="animate-pulse">▊</span>
: '...'
)}
</div>
</div>
))}
<div ref={bottomRef} /> {/* scroll anchor */}
</div>
{/* Input area ⌨️ */}
<div className="border-t pt-4 flex gap-2">
<textarea
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={handleKey}
placeholder="Ask anything... (Enter to send)"
disabled={loading}
rows={1}
className="flex-1 resize-none border rounded-xl px-4 py-3 text-sm
focus:outline-none focus:ring-2 focus:ring-blue-500
disabled:opacity-50"
/>
<button
onClick={sendMessage}
disabled={loading || !input.trim()}
className="px-5 py-3 bg-blue-600 text-white rounded-xl font-medium
hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed
transition-colors text-sm"
>
{loading ? '⏳' : '🚀'}
</button>
</div>
</div>
)
}
Total: ~95 lines. Full working chatbot. Done. ✅
🛠️ Step 4 — Deploy in 5 Minutes 🌐
# Push to GitHub first 📤
git init
git add .
git commit -m "feat: add AI chatbot 🤖"
git remote add origin https://github.com/you/ai-chatbot.git
git push -u origin main
Then on Vercel: 👇
- Go to vercel.com → New Project
- Import your GitHub repo
- Add environment variable:
OPENAI_API_KEY= your key 🔑 - Click Deploy ✅
5 minutes. Live URL. Done. 🎉
Your chatbot is now live at:
https://ai-chatbot-yourname.vercel.app 🌐
Share it. Add it to your resume. Demo it in interviews.
🎨 Level Up — 5 Easy Customizations
The base chatbot is great. These 5 tweaks make it portfolio-worthy. 💎
Customization 1 — Change the Personality 🎭
// Change the system prompt = completely different product 🎨
// 🍳 Recipe assistant
content: `You are a friendly chef who suggests recipes based on
ingredients the user has. Always ask what ingredients
they have before suggesting. Give step-by-step instructions.`
// 💪 Fitness coach
content: `You are an encouraging fitness coach. Create personalized
workout plans. Always ask about fitness level and goals first.
Use motivational language. 💪`
// 😂 Sarcastic code reviewer
content: `You are a brutally honest (but funny) code reviewer.
Point out every issue with light sarcasm. Always end with
something genuinely helpful. Keep it fun, not mean.`
// 🎓 Interview prep coach
content: `You are a senior developer conducting mock interviews.
Ask technical questions one at a time. Give detailed feedback
on every answer. Be encouraging but honest about gaps.`
💡 Portfolio tip: Each system prompt = a different product. Build 3 chatbots with different personalities = 3 portfolio projects. Same code, different value propositions. 🎯
Customization 2 — Add a Clear Chat Button 🗑️
// Add this button next to the header — 3 lines 🧹
<button
onClick={() => setMessages([])}
className="text-sm text-gray-400 hover:text-gray-600"
>
🗑️ Clear chat
</button>
Customization 3 — Show Message Timestamps ⏰
// Update Message type
type Message = {
role: 'user' | 'assistant'
content: string
timestamp: Date // add this ⏰
}
// Add timestamp when creating messages
const userMessage: Message = {
role: 'user',
content: input,
timestamp: new Date() // ✅
}
// Show it in the bubble
<div className="text-xs opacity-50 mt-1">
{msg.timestamp.toLocaleTimeString()}
</div>
Customization 4 — Copy Message Button 📋
// Add copy button to each AI message — super useful feature ✨
const [copied, setCopied] = useState<number | null>(null)
const copyMessage = async (text: string, index: number) => {
await navigator.clipboard.writeText(text)
setCopied(index)
setTimeout(() => setCopied(null), 2000) // reset after 2s
}
// In the AI message bubble:
{msg.role === 'assistant' && (
<button
onClick={() => copyMessage(msg.content, i)}
className="text-xs text-gray-400 hover:text-gray-600 mt-1"
>
{copied === i ? '✅ Copied!' : '📋 Copy'}
</button>
)}
Customization 5 — Suggested Starter Questions 💡
// Show clickable starter questions when chat is empty 🎯
const starters = [
"How do I use useEffect correctly? 🤔",
"What's the difference between SSR and SSG?",
"How do I handle forms in React? 📝",
"Explain React hooks for a beginner 🪝"
]
// In the empty state:
{messages.length === 0 && (
<div className="space-y-2 mt-6">
{starters.map((q, i) => (
<button
key={i}
onClick={() => setInput(q)}
className="w-full text-left px-4 py-3 border rounded-xl
text-sm text-gray-600 hover:bg-gray-50 transition-colors"
>
{q}
</button>
))}
</div>
)}
📊 What You Just Built vs What Others Think You Built
What you actually built 👇
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
~95 lines of code
1 API route
1 React component
30 minutes of actual coding
What interviewers THINK when they see it 👇
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"This person knows AI APIs" ✅
"They understand streaming" ✅
"They can build full-stack features" ✅
"They care about UX (auto-scroll)" ✅
"They know TypeScript" ✅
"This is deployed and working" ✅
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Perception: Senior-level thinking 🎯
Reality: 95 lines + 30 minutes 😄
The gap between what you build and what people perceive is massive. Use it. 🚀
🎯 Challenge — Build It Right Now
Set a timer for 90 minutes. 👇
0:00 → npx create-next-app@latest ai-chatbot
0:10 → Write the API route (20 lines)
0:35 → Write the chat UI (75 lines)
1:10 → Deploy on Vercel
1:25 → Add your favorite customization
1:30 → Share the link in comments below 👇
90 minutes. Working AI chatbot. Live URL. On your resume. 🏁
💬 Your Turn!
Built it? 🎉 Drop your live link in the comments — I'll check every single one!
Stuck on something? 🤔 Drop your error in the comments — let's debug it together!
Planning to build but haven't started yet? ⏰ Drop a ⏱️ — I'll check back in 90 minutes!
Share this with someone who's been "planning to learn AI" for months. Today is the day. 🙏
Drop a ❤️ if this finally made AI feel approachable — helps more developers find this before they give up! 🔥
🔖 P.S. — The system prompt is the most powerful line in this entire codebase. Change it. Play with it. That one line is what separates a generic chatbot from a product people actually want to use.
Top comments (0)