Full Stack 42:182025-12-10
Next.js App Router: The Complete Guide for 2026
Everything you need to know about the App Router — server components, client components, layouts, loading states, and data fetching patterns that actually work in production.
Watch on YouTube
Click to open in YouTube
Video Notes & Code
Key takeaways, code snippets, and resources from this video.
Server Components vs Client Components
The fundamental rule: if a component reads from a database, filesystem, or env variable — make it a server component. If it uses useState, useEffect, onClick, or browser APIs — add "use client".
tsx
// Server Component — no "use client" needed
async function ProductList() {
const products = await db.query('SELECT * FROM products')
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
// Client Component — needs browser interaction
"use client"
import { useState } from 'react'
function SearchBar() {
const [query, setQuery] = useState('')
return <input value={query} onChange={e => setQuery(e.target.value)} />
}Data Fetching Patterns
- Fetch data in server components directly — no useEffect needed
- Use React cache() to deduplicate requests across the component tree
- Use loading.tsx for streaming suspense boundaries
- Use generateStaticParams for static routes with dynamic segments
tsx
// app/products/[id]/page.tsx
import { cache } from 'react'
const getProduct = cache(async (id: string) => {
return db.query('SELECT * FROM products WHERE id = $1', [id])
})
export async function generateStaticParams() {
const products = await db.query('SELECT id FROM products')
return products.map(p => ({ id: p.id }))
}
export default async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id)
return <div>{product.name}</div>
}Found this useful?
Subscribe for weekly content on AI engineering, SaaS building, and full stack development.