"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)