# Les 5 — Lesstof
## Next.js Basics — het React framework
**Vak:** Technical Foundations
**Vorige les:** Les 4 — TypeScript Fundamentals
**Volgende les:** Les 6 — Next.js QuickPoll deel 2
---
## Inhoud
1. [Waarom Next.js](#1-waarom-nextjs)
2. [App Router vs Pages Router](#2-app-router-vs-pages-router)
3. [Setup — eerste project](#3-setup--eerste-project)
4. [Folder-structuur](#4-folder-structuur)
5. [Pages + Layouts](#5-pages--layouts)
6. [Server Components vs Client Components](#6-server-components-vs-client-components)
7. [Routing — dynamic + nested](#7-routing--dynamic--nested)
8. [Data fetching basics](#8-data-fetching-basics)
9. [Styling met Tailwind](#9-styling-met-tailwind)
10. [Loading + Error states](#10-loading--error-states)
11. [QuickPoll — het project van deze les](#11-quickpoll--het-project-van-deze-les)
---
## 1. Waarom Next.js
React alleen is een UI-library — niet meer. Voor een echte website heb je nodig:
- Routing (welke URL → welke pagina)
- Server-side rendering (SEO + snelle first paint)
- API endpoints
- Image optimization, fonts, caching
- Deployment
**Next.js** voegt dit allemaal toe bovenop React. Standaard framework van Vercel.
### Waarom Next.js voor deze leerlijn
- Industrie-standaard sinds 2022
- Werkt naadloos met Vercel deploy (Les 15)
- Server Components + Client Components — modern paradigm
- TypeScript first-class
- AI tools (Cursor, v0) zijn geoptimaliseerd voor Next.js
Andere keuzes (Remix, Astro, SvelteKit) bestaan, maar Next.js is de breedst gebruikte.
---
## 2. App Router vs Pages Router
Next.js heeft twee router-systemen — historisch gegroeid:
### Pages Router (legacy, sinds 2016)
- `pages/` folder
- File = route. `pages/about.tsx` → `/about`
- `_app.tsx`, `_document.tsx`, `getServerSideProps`, ...
### App Router (sinds Next.js 13, default sinds 14)
- `app/` folder
- File = route. `app/about/page.tsx` → `/about`
- Server Components by default
- Layouts, nested routing, loading states als file
- React Server Components paradigm
**In deze leerlijn: App Router.** Alle nieuwe projecten = App Router.
---
## 3. Setup — eerste project
### Create
```bash
pnpm create next-app@latest quickpoll \
--typescript --tailwind --app --no-src-dir --import-alias "@/*"
cd quickpoll
pnpm dev
```
Open `localhost:3000` → werkt.
### Wat krijg je out-of-the-box
- TypeScript geconfigureerd
- Tailwind CSS v4
- ESLint
- Hot reloading
- File-based routing
- Optimized fonts (Geist)
---
## 4. Folder-structuur
```
quickpoll/
├── app/
│ ├── layout.tsx # Root layout — wraps every page
│ ├── page.tsx # Homepage (/)
│ ├── globals.css # Tailwind imports
│ ├── about/
│ │ └── page.tsx # /about
│ └── poll/
│ └── [id]/
│ └── page.tsx # /poll/123 (dynamic)
├── components/ # Herbruikbare components
├── lib/ # Utility code
├── public/ # Static files
├── next.config.ts
├── tsconfig.json
└── package.json
```
### File-conventies
| Filename | Wat |
|----------|-----|
| `page.tsx` | De pagina-component voor deze route |
| `layout.tsx` | Wraps children met persistent UI |
| `loading.tsx` | Loading state |
| `error.tsx` | Error boundary |
| `not-found.tsx` | 404 voor deze route |
| `route.ts` | API endpoint |
---
## 5. Pages + Layouts
### Page
```tsx
// app/about/page.tsx
export default function AboutPage() {
return
About us
;
}
```
### Layout
```tsx
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
Layouts zijn **persistent** — niet ge-remount tussen pagina-navigaties. State blijft.
### Nested layouts
```
app/
├── layout.tsx # Root — alle pagina's
├── (marketing)/
│ ├── layout.tsx # Layout voor marketing pages
│ ├── page.tsx
│ └── about/page.tsx
└── dashboard/
├── layout.tsx # Layout voor dashboard
└── page.tsx
```
`(marketing)` = "route group" — geen URL-segment, alleen voor file-organisatie.
---
## 6. Server Components vs Client Components
App Router's grootste paradigm-shift.
### Server Component (default)
```tsx
// app/page.tsx — runt op de server
export default async function Home() {
const data = await fetch("https://api.example.com/posts").then(r => r.json());
return
{data.map(p =>
{p.title}
)}
;
}
```
- Draait alleen op server
- Geen `useState`, geen `useEffect`, geen browser-APIs
- Mag `async` zijn — `await` data fetching direct
- Geen JavaScript naar client (kleinere bundle)
### Client Component
```tsx
// components/Counter.tsx
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return ;
}
```
- `"use client"` directive bovenaan
- Gewoon React — hooks, events, state
- Wordt JavaScript naar browser
### Wanneer welke
| | Server | Client |
|---|--------|--------|
| Data fetching | ✓ | (kan, niet ideaal) |
| Hooks (useState, etc.) | ✗ | ✓ |
| Event handlers (onClick) | ✗ | ✓ |
| Browser APIs (localStorage) | ✗ | ✓ |
| SEO content | ✓ | (werkt, minder ideaal) |
**Vuistregel:** Server by default. `"use client"` alleen als nodig.
---
## 7. Routing — dynamic + nested
### Static route
```
app/about/page.tsx → /about
```
### Dynamic route
```
app/poll/[id]/page.tsx → /poll/abc-123
```
In de component:
```tsx
export default async function Poll({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return
Poll {id}
;
}
```
Vanaf Next.js 16: `params` is een Promise (was object).
### Catch-all routes
```
app/docs/[...slug]/page.tsx → /docs/a/b/c
```
`params.slug` = `["a", "b", "c"]`.
### Link tussen pages
```tsx
import Link from "next/link";
About
Bekijk poll
```
`Link` = client-side navigatie (geen full page reload).
---
## 8. Data fetching basics
### In Server Component
```tsx
async function getPolls() {
const res = await fetch("https://api.example.com/polls", {
next: { revalidate: 60 }, // cache 60 sec
});
return res.json();
}
export default async function Home() {
const polls = await getPolls();
return ;
}
```
### Cache opties
```tsx
fetch(url, { cache: "force-cache" }) // permanent (default)
fetch(url, { next: { revalidate: 60 } }) // ISR — herval na 60s
fetch(url, { cache: "no-store" }) // nooit cachen (live)
```
### In Client Component
```tsx
"use client";
import { useEffect, useState } from "react";
export function Live() {
const [data, setData] = useState(null);
useEffect(() => {
fetch("/api/data").then(r => r.json()).then(setData);
}, []);
return data ?
{data.value}
:
Loading...
;
}
```
Voor interactiviteit. Of via TanStack Query (komt later).
---
## 9. Styling met Tailwind
Tailwind = utility-first CSS framework. Class per CSS-property.
### Basics
```tsx
Hello
World
```
### Responsive
```tsx
```
- Default = mobile
- `md:` = tablet+ (768px+)
- `lg:` = desktop+ (1024px+)
### Hover, focus, dark
```tsx