By Dhanian
Artificial Intelligence is transforming how we interact with applications—and as a developer, I wanted to dive in and experience that firsthand. So I challenged myself to build a ChatGPT-like application using Next.js 15, TypeScript, TailwindCSS, and the OpenAI API.
Spoiler alert: it was surprisingly simple—and incredibly fun. Here's everything I learned, how I built it step-by-step, and what you should know if you're planning to build one too.
Why Build a ChatGPT Clone?
I chose this project because:
- It's a real-world application of AI.
- It helps you learn API integration, UI/UX design, state management, and async communication.
- It’s highly customizable—you can turn it into a customer support bot, coding assistant, or even a SaaS tool.
Tech Stack Breakdown
Here’s what I used:
- Next.js 15 (App Router): For server/client rendering and routing.
- TailwindCSS: For quick and responsive UI styling.
- OpenAI API: For chat capabilities.
- TypeScript: For type safety and better dev experience.
- React Hooks (useState, useEffect): To manage messages.
Project Architecture
/chatgpt-clone
│
├── app/
│ ├── page.tsx --> Main chat page
│ ├── api/
│ │ └── chat/route.ts --> API route to OpenAI
│
├── components/
│ ├── ChatWindow.tsx
│ ├── Message.tsx
│
├── lib/
│ └── openai.ts --> OpenAI config
│
├── styles/
│ └── globals.css
Step-by-Step Guide
1. Set Up the Next.js Project
npx create-next-app@latest chatgpt-clone --typescript --app
cd chatgpt-clone
npm install openai tailwindcss
Set up Tailwind:
npx tailwindcss init -p
Edit tailwind.config.js
and add paths to your files.
2. Create the Chat UI
/app/page.tsx
:
"use client"
import { useState } from "react"
export default function HomePage() {
const [messages, setMessages] = useState([{ role: "system", content: "How can I help you today?" }])
const [input, setInput] = useState("")
async function sendMessage() {
const newMessages = [...messages, { role: "user", content: input }]
setMessages(newMessages)
setInput("")
const res = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ messages: newMessages }),
})
const data = await res.json()
setMessages([...newMessages, data.reply])
}
return (
<main className="min-h-screen p-6 bg-gray-100">
<div className="max-w-2xl mx-auto bg-white p-4 rounded shadow">
<div className="space-y-2 mb-4 h-[400px] overflow-y-auto">
{messages.map((m, i) => (
<div key={i} className={`text-${m.role === "user" ? "right" : "left"}`}>
<p className={`text-sm p-2 rounded ${m.role === "user" ? "bg-blue-100" : "bg-gray-200"}`}>
{m.content}
p>
div>
))}
div>
<div className="flex gap-2">
<input
className="flex-1 border p-2 rounded"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={sendMessage} className="bg-blue-500 text-white px-4 rounded">Sendbutton>
div>
div>
main>
)
}
3. Connect to OpenAI API
/app/api/chat/route.ts
:
import { OpenAI } from "openai"
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
})
export async function POST(req: Request) {
const { messages } = await req.json()
const response = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
messages,
})
const reply = response.choices[0].message
return Response.json({ reply })
}
Make sure you add your API key in .env.local
:
OPENAI_API_KEY=your_key_here
What I Learned
- Rate limits are a real concern when working with OpenAI’s free tier.
- Streaming responses are the future—instant feedback improves UX.
- Next.js App Router is incredibly clean for separating logic.
- It’s super easy to build SaaS-like tools using AI APIs.
What’s Next?
- Add authentication using NextAuth.
- Save chat history to Supabase or MongoDB.
- Enable response streaming for real-time interaction.
- Upgrade to GPT-4 for premium power users.
Final Thoughts
Building this ChatGPT clone was one of the most exciting and practical mini-projects I’ve done. If you’re a developer looking to experiment with AI, there’s no better way to start.
Everything I Build Is Available Here:
- Source code, ebooks, and full-stack resources: codewithdhanian.gumroad.com
- Follow me on X for daily dev content & updates: @e_opore
Let’s build cool stuff together!