Next.js
React with superpowers. File-based routing, server components Server A computer (or program) that provides data, services, or resources to other computers over a network. When you visit a website, a server sends the page to your browser. "Like a restaurant kitchen. You (the client) order food, and the kitchen (server) prepares and delivers it to you."
What is Next.js?
Next.js is a React framework created by Vercel. While React is a library for building UI components, Next.js adds everything you need for production: routing, server-side rendering, API endpoints, and optimization.
React alone
- • Just the UI layer
- • No routing built-in
- • Client-side only
- • You configure everything
Next.js
- • Full framework
- • File-based routing
- • Server + Client rendering
- • Zero-config, just works
Why Vibe Coders Love It
Next.js is what Claude Code knows best. The file structure is predictable, conventions are clear, and AI can navigate your project easily.
The App Router
Next.js 13+ introduced the App Router — a new way to build apps using the /app directory. It's the default and recommended approach.
Pages Router vs App Router
You might see tutorials using /pages — that's the old Pages Router. It still works, but always use App Router for new projects. It's more powerful and the future of Next.js.
File-Based Routing
Your folder structure = your URL structure. Create a folder, add a page.tsx, and you have a route.
app/
page.tsx → yoursite.com/
about/
page.tsx → yoursite.com/about
blog/
page.tsx → yoursite.com/blog
[slug]/
page.tsx → yoursite.com/blog/any-post
Dynamic Routes
[slug] creates a dynamic segment. The value becomes available as a parameter in your component.
Key Files
page.tsxRequiredThe UI for a route. This is what users see when they visit that URL. Every route needs a page.tsx.
layout.tsxShared UIWraps pages in shared UI (navbar, footer, sidebar). Persists across navigation — doesn't re-render.
export default function RootLayout({
children
}: { children: React.ReactNode }) {
return (
<html>
<body>
<Navbar />
{children}
<Footer />
</body>
</html>
)
}
loading.tsxLoading StateAutomatic loading UI shown while a page or layout is loading. Uses React Suspense under the hood.
error.tsxError BoundaryCatches errors in a route segment and shows a fallback UI. Prevents the whole app from crashing.
not-found.tsx404 PageCustom 404 page shown when a route doesn't exist or when you call notFound().
Server vs Client Components
⚠️ This Is The #1 Confusion Point
Understanding when to use Server vs Client components is the key to mastering Next.js. Read this section carefully.
Server Components
Default in App Router. Render on the server, send HTML to browser. No JavaScript shipped.
✓ Fetch data directly
✓ Access backend resources
✓ Keep secrets secure
✓ Smaller bundle size
✗ No useState, useEffect
✗ No onClick, onChange
✗ No browser APIs
Client Components
Add "use client" at top. Render in browser. Full React interactivity.
✓ useState, useEffect
✓ Event handlers (onClick)
✓ Browser APIs
✓ Third-party UI libraries
✗ Can't access backend directly
✗ Larger bundle size
✗ Secrets exposed if included
The Rule of Thumb
Start with Server Components. Only add "use client" when you NEED interactivity.
When to Use "use client"
"use client" // ← Add this at the VERY TOP of the file
import { useState } from "react"
export default function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
Use "use client" when you need:
- • useState, useEffect, useRef
- • onClick, onChange, onSubmit
- • Framer Motion animations
- • Form libraries (react-hook-form)
- • Browser APIs (localStorage)
Keep as Server Component when:
- • Fetching data from database
- • Reading files
- • Static content display
- • SEO-critical content
- • Using environment secrets
Data Fetching
In Server Components, you can fetch data directly using async/await. No useEffect needed!
// app/users/page.tsx (Server Component by default)
async function
getUsers() {
const res = await fetch('https://api.example.com/users')
return res.json()
}
export default async function
UsersPage() {
const users = await getUsers() // Direct await!
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
}
Why This Is Amazing
No loading states to manage, no useEffect, no client-side fetch. Data is fetched on the server, HTML is sent to browser. Fast and SEO-friendly.
API Routes & Server Actions
API Routesroute.ts
Build your backend API in Next.js. Create route.ts files that handle HTTP requests.
// app/api/users/route.ts
import { NextResponse } from 'next/server'
export async function
GET() {
const users = await db.query('SELECT * FROM users')
return NextResponse.json(users)
}
export async function
POST(request: Request) {
const body = await request.json()
// Create user...
return NextResponse.json({ success: true })
}
Server ActionsNew & Recommended
Call server functions directly from client components. No API route needed. Use "use server".
// app/actions.ts
"use server"
export async function
createUser(formData: FormData) {
const name = formData.get('name')
await db.insert(users).values({ name })
revalidatePath('/users') // Refresh data
}
// In your form (Client Component)
<form action={createUser}>
<input name="name" />
<button type="submit">Create</button>
</form>
Recommended Project Structure
my-app/
app/ # Routes & pages
layout.tsx # Root layout
page.tsx # Homepage
globals.css # Global styles
dashboard/
page.tsx
api/
users/route.ts
components/ # Reusable UI
Navbar.tsx
Button.tsx
lib/ # Utilities & config
db.ts # Database connection
utils.ts
public/ # Static files
logo.png
.env.local # Environment vars
package.json
next.config.js
tailwind.config.ts
Common Pitfalls
"useState is not defined"
You're using React hooks in a Server Component.
Fix: Add "use client" at the top of the file.
"async/await not working in component"
You're trying to make a Client Component async.
Fix: Only Server Components can be async. Use useEffect + fetch for client-side data, or fetch in a Server Component parent.
"Hydration mismatch"
Server HTML doesn't match client HTML. Often from using browser APIs (window, localStorage) during render.
Fix: Wrap browser API calls in useEffect, or check typeof window !== 'undefined'.
"Can't import Server Component into Client Component"
Client Components can't import Server Components directly.
Fix: Pass Server Components as children or props to Client Components.
"Environment variable is undefined"
Client Components can only access env vars prefixed with NEXT_PUBLIC_.
Fix: Use NEXT_PUBLIC_ prefix for client vars, or access secrets only in Server Components/API routes.
Quick Start
# Create new Next.js app
npx create-next-app@latest my-app
# Answer the prompts:
✔ TypeScript? Yes
✔ ESLint? Yes
✔ Tailwind CSS? Yes
✔ src/ directory? No
✔ App Router? Yes
✔ Import alias? Yes (@/*)
# Start development server
cd my-app && npm run dev
Open http://localhost:3000 and start building!