Chapter 20: Server-Side Rendering & Next.js Basics
Server-Side Rendering & Next.js Basics — this is the chapter that takes you from frontend-only React to full-stack, production-ready web applications!
Next.js is the most popular React framework in 2026 — used by companies like Netflix, TikTok, Vercel, OpenAI, and thousands of others. It gives you server-side rendering (SSR), static site generation (SSG), API routes, image optimization, automatic code splitting, and much more — all while keeping the developer experience amazing.
We’ll go very slowly and clearly, like I’m sitting next to you in Mumbai with our laptops open, explaining everything live with complete, copy-paste-ready examples.
1. Introduction to Next.js – What & Why?
Next.js is a React framework created by Vercel that adds powerful features on top of React:
- Server-side rendering (SSR) → better SEO & faster first paint
- Static site generation (SSG) → super fast & cheap hosting
- Incremental Static Regeneration (ISR) → static + dynamic updates
- App Router (new in v13+) → modern file-system-based routing
- API Routes → build backend endpoints inside your app
- Image Optimization, Font Optimization, Internationalization, Middleware…
- Deploy anywhere (Vercel is easiest, but also Netlify, Cloudflare, AWS…)
Why learn Next.js in 2026?
- Almost every React job asks for Next.js
- It’s the official React recommendation for production apps
- You write normal React — but get superpowers
2. Create Your First Next.js Project (2026 Way)
|
0 1 2 3 4 5 6 7 8 |
npx create-next-app@latest my-next-app cd my-next-app npm run dev |
→ Opens at http://localhost:3000
You’ll see a beautiful starter page — now let’s understand the two routing systems.
3. App Router vs Pages Router (Important!)
Next.js has two routing systems:
| Feature | App Router (recommended since v13 – 2026 standard) | Pages Router (older, still supported) |
|---|---|---|
| Location | app/ folder | pages/ folder |
| Routing style | File-system + folders (like Remix) | File-system (every file = route) |
| Server Components | Default (React Server Components) | No |
| Data fetching | async components + fetch with caching | getStaticProps, getServerSideProps |
| Layouts | Nested layouts with layout.tsx | Manual or _app.tsx |
| Loading/Error UI | Built-in loading.tsx, error.tsx | Manual |
| Streaming & Suspense | Native support | No |
| Current recommendation | Use this for all new projects | Only for legacy code |
Bottom line (2026): Always use App Router for new projects — it’s more powerful, faster, and future-proof.
4. Static vs Server Components (The Biggest Change in Next.js 13+)
In App Router, React introduced React Server Components (RSC).
| Feature | Server Components (default) | Client Components (opt-in) |
|---|---|---|
| Where they run | On the server (no JS sent to browser) | On the client (like normal React) |
| Can use hooks? | No (useState, useEffect, useRef…) | Yes |
| Can fetch data? | Yes (directly in component) | Yes (but better to fetch on server) |
| Can access backend secrets? | Yes (API keys, database…) | No |
| SEO & performance | Excellent (HTML sent from server) | Good, but slower first paint |
| How to mark | Default — no directive | Add ‘use client’; at top of file |
Rule of thumb:
- Use Server Components by default (fetch data, render HTML)
- Use Client Components only when you need interactivity (useState, onClick, useEffect)
5. Data Fetching in Next.js (App Router – Modern Way)
Next.js extends the native fetch API with smart caching and revalidation.
Example 1: Static Data Fetching (SSG – Build-time)
Create app/page.tsx
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
async function getUsers() { const res = await fetch('https://jsonplaceholder.typicode.com/users', { next: { revalidate: 3600 } // revalidate every 1 hour (ISR) }); if (!res.ok) throw new Error('Failed to fetch users'); return res.json(); } export default async function Home() { const users = await getUsers(); return ( <div className="container mx-auto p-8"> <h1 className="text-4xl font-bold mb-8 text-indigo-600">Users (Static + ISR)</h1> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> {users.map((user: any) => ( <div key={user.id} className="p-6 bg-white border rounded-xl shadow-md hover:shadow-lg transition-shadow" > <h2 className="text-xl font-semibold">{user.name}</h2> <p className="text-gray-600">{user.email}</p> <p className="text-sm text-gray-500 mt-2">Company: {user.company.name}</p> </div> ))} </div> </div> ); } |
→ This page is statically generated at build time + revalidated every hour (Incremental Static Regeneration)
Example 2: Dynamic Data Fetching (SSR)
Create app/users/[id]/page.tsx (dynamic route)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
interface User { id: number; name: string; email: string; company: { name: string }; } async function getUser(id: string) { const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`, { cache: 'no-store' // force SSR – no caching }); if (!res.ok) throw new Error('Failed to fetch user'); return res.json(); } export default async function UserPage({ params }: { params: { id: string } }) { const user: User = await getUser(params.id); return ( <div className="container mx-auto p-8"> <h1 className="text-4xl font-bold mb-6 text-indigo-600">{user.name}</h1> <div className="bg-white p-8 rounded-xl shadow-lg max-w-2xl"> <p className="text-lg mb-4"><strong>Email:</strong> {user.email}</p> <p className="text-lg mb-4"><strong>Company:</strong> {user.company.name}</p> <p className="text-lg"><strong>Website:</strong> {user.website}</p> </div> </div> ); } |
→ This page is rendered on the server for every request (SSR)
Bonus: Loading UI (Streaming)
Create app/users/[id]/loading.tsx
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
export default function Loading() { return ( <div className="container mx-auto p-8"> <div className="animate-pulse"> <div className="h-12 bg-gray-200 rounded w-1/3 mb-6"></div> <div className="bg-white p-8 rounded-xl shadow-lg"> <div className="h-6 bg-gray-200 rounded w-3/4 mb-4"></div> <div className="h-6 bg-gray-200 rounded w-1/2 mb-4"></div> <div className="h-6 bg-gray-200 rounded w-2/3"></div> </div> </div> </div> ); } |
→ Shows nice skeleton while data loads!
Summary – Chapter 20 Key Takeaways
- Next.js = React framework for SSR, SSG, ISR, API routes
- App Router (app/ folder) = modern way (use this!)
- Server Components = default, run on server, no JS sent
- Client Components = ‘use client’; when you need hooks/interactivity
- Data fetching:
- fetch with cache: ‘no-store’ → SSR
- fetch with next: { revalidate: 3600 } → ISR
- fetch with no options → static
- loading.tsx, error.tsx → automatic loading & error UI
Mini Homework
- Create a Next.js app with:
- Home page: list of users (static + ISR)
- Dynamic /users/[id] page (SSR)
- Nice loading skeleton
- Bonus: Add a simple API route /api/hello that returns JSON
