I Built a ChatGPT Clone Using Next.js & OpenAI API – Here’s What Happened
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 ( {messages.map((m, i) => ( {m.content} ))} setInput(e.target.value)} /> Send ) } 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!

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!