Files
novi-lessons/Les05-NextJS-Basics/Les05-Slide-Overzicht.md
2026-03-11 14:07:00 +01:00

975 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Les 5: Next.js — Het React Framework - Slide Overzicht (Part 1)
> Versie 2 — ~45 minuten theorie + 15 min pauze + ~120 min klassikaal bouwen
>
> **Dit is Part 1 van 2.** Part 2 wordt behandeld in Les 6 (Blok 4: advanced features, Stap 4-7 QuickPoll, deployment).
---
## Slide 1: Titel - "Les 5: Next.js — Het React Framework"
### Op de Slide
- Titel: **Les 5: Next.js — Het React Framework**
- Subtitel: "Van React naar Productie"
- **Les 5 van 18** (progress indicator)
- Next.js logo + Vercel logo
- Dark background, modern feel
### Docentnotities
Tim opent enthousiast en maakt connectie met vorige lessen.
"Welkom bij Les 5! De afgelopen weken hebben jullie HTML, CSS, JavaScript, en TypeScript geleerd. Vandaag maken we een grote stap: we gaan van losse technologieën naar een echt framework. Next.js."
*Pauze voor effect.*
"Na vandaag kun je een complete webapplicatie bouwen. Met routing, API's, middleware — alles. En het mooiste? Het is gebouwd op React, dus alles wat je al kent werkt gewoon."
---
## Slide 2: Planning Vandaag
### Op de Slide
- **Blok 1 (15 min)**: Waarom Next.js?
- Het probleem met pure React
- Wat Next.js oplost
- **Blok 2 (15 min)**: App Router & Project Structuur
- Folder-based routing, layouts, dynamic routes
- Route Groups & best practices
- **Blok 3 (15 min)**: Server vs Client Components, Data Fetching, Server Actions
- Server Components: async, geen JS naar browser
- Data fetching patterns
- Intro Server Actions
- **PAUZE (15 min)** ☕
- **Klassikaal Bouwen (~120 min)**: QuickPoll App Part 1 (Stap 0-3)
- Tim + klas werken samen
- Studenten volgen mee in hun eigen project
- Cursor Tab-completion mag gebruikt worden
**Totaal: ~3 uur. Part 1 → Blok 1-3 + Stap 0-3. Part 2 (Les 6) → Blok 4 + Stap 4-7 + Deployment.**
### Docentnotities
Tim loopt door de planning.
"Dit is het plan voor vandaag. Drie blokken theorie: waarom Next.js, hoe routing werkt, en de kern—server vs client components en data fetching. Dan pauze."
"Daarna doen we het ANDERS dan vorige keer. In plaats van dat jullie allemaal zelfstandig werken, gaan we SAMEN een app bouwen. Ik code op het scherm, jullie volgen mee op jullie eigen laptop. Dat voelt beter dan solo werk, toch?"
"We bouwen QuickPoll Part 1 — dat is de eerste helft van de app. Part 2 met deployment en ingewikkelder features doen we volgende les. Zo hebben jullie vandaag meer tijd om echt te begrijpen wat er gebeurt."
---
## Slide 3: Terugblik Les 4 — TypeScript
### Op de Slide
- **Les 4 samengevat:**
- TypeScript = JavaScript + Types
- Interfaces, Union Types, Type Narrowing
- Escaperoom: 10 kamers, 49 errors
- **Key takeaway**: Types voorkomen bugs vóórdat je code draait
- **Vandaag**: TypeScript in actie binnen een framework
### Docentnotities
Tim doet een korte terugblik.
"Vorige week hebben jullie TypeScript geleerd. Types, interfaces, unions, narrowing — en natuurlijk die escaperoom. Wie heeft alle 10 kamers gehaald?"
*Pauze voor reacties.*
"Nice. Die TypeScript kennis gaan we vandaag direct gebruiken. Next.js is volledig TypeScript-first. Alles wat je schrijft in Next.js is getypt. Dus jullie zijn al voorbereid."
---
## Slide 4: Het Probleem met Pure React
### Op de Slide
- **React is geweldig, MAAR:**
- ❌ Geen ingebouwde routing (react-router nodig)
- ❌ Geen server-side rendering (slecht voor SEO)
- ❌ Geen API routes (aparte backend nodig)
- ❌ Geen file-based structuur (je moet alles zelf organiseren)
- ❌ Geen image optimization
- ❌ Geen built-in performance optimizations
- **Het resultaat:**
- React app = SPA (Single Page Application)
- Alles draait in de browser
- Google ziet een lege pagina
### Docentnotities
Tim legt het probleem uit met concrete voorbeelden.
"Oké, React is fantastisch voor UI bouwen. Maar als je een échte website wilt maken — eentje die Google kan indexeren, die snel laadt, die een API heeft — dan loop je tegen grenzen aan."
"Stel je bouwt een webshop met React. Je hebt routing nodig: react-router. Je wilt dat Google je producten vindt: SSR, maar React doet dat niet standaard. Je wilt een API voor je producten: dan moet je een aparte Express server opzetten. Je wilt images optimizen: weer een extra library."
*Tim telt op zijn vingers.*
"Dus je bent meer bezig met configuratie dan met je app bouwen. En dat is precies het probleem dat Next.js oplost."
---
## Slide 5: Next.js = Het React Framework
### Op de Slide
- **Next.js biedt alles out-of-the-box:**
-**File-based Routing** — folder = route
-**Server-Side Rendering (SSR)** — HTML op de server
-**API Routes** — backend in hetzelfde project
-**Middleware** — logic vóór de request
-**Image Optimization** — next/image
-**TypeScript-first** — zero config
-**Built-in CSS/Tailwind** — styling out-of-the-box
- **Gemaakt door Vercel** — het bedrijf achter de hosting
### Docentnotities
Tim bouwt enthousiasme op.
"Next.js pakt al die problemen en lost ze op in één framework. Routing? Maak een folder. API? Maak een `route.ts` bestand. Server-side rendering? Standaard aan. TypeScript? Zero configuratie."
"Het is gemaakt door Vercel — dat is ook een hosting platform. Dus van development tot deployment, het hele verhaal zit erin."
"En het belangrijkste: Next.js is gebouwd OP React. Je schrijft gewoon React components. Alles wat je kent — useState, useEffect, JSX — werkt gewoon. Next.js voegt er alleen superkrachten aan toe."
---
## Slide 6: Wie Gebruikt Next.js?
### Op de Slide
- **Grote bedrijven op Next.js:**
- 🎬 Netflix — marketing pages
- 🎵 TikTok — web app
- 📝 Notion — website & docs
- 🛒 Nike — webshop
- 📊 Twitch — dashboard
- 🏢 Hulu, Hashicorp, Auth0, ...
- **Waarom?**
- Performance, SEO, Developer Experience
- Snel itereren, snel deployen
- Logo wall van bedrijven
### Docentnotities
Tim laat zien dat Next.js geen speelgoed is.
"Dit zijn geen kleine startups. Netflix, TikTok, Nike — ze vertrouwen op Next.js voor hun websites. Waarom? Omdat het snel is, goed voor SEO, en developers er productief mee zijn."
"Als jullie straks solliciteren en Next.js op je CV staat, dan weten bedrijven: die kan een complete webapplicatie bouwen. Dat is wat deze les zo waardevol maakt."
---
## Slide 7: create-next-app — Snel Starten
### Op de Slide
```bash
npx create-next-app@latest mijn-app
```
- **Opties die je krijgt:**
- ✅ TypeScript
- ✅ Tailwind CSS
- ✅ App Router
-`src/` directory
- ❌ ESLint (optioneel)
- **Project structuur klaar:**
```
mijn-app/
├── src/app/
│ ├── layout.tsx ← root layout
│ ├── page.tsx ← homepage
│ └── globals.css
├── public/
├── package.json
├── tsconfig.json
└── next.config.ts
```
### Docentnotities
Tim toont hoe snel je begint.
"Create-next-app zet alles voor je klaar. TypeScript, Tailwind, App Router — nul configuratie. Je typt het commando en binnen tien seconden heb je een werkend project."
"Straks gaan we dit samen doen. Dan zie je: `npm run dev`, dan staat je app op localhost:3000. Klaar."
---
## Slide 8: App Router — Folder = Route
### Op de Slide
- **De gouden regel:** Elke folder in `app/` is een route
```
src/app/
├── page.tsx → /
├── about/
│ └── page.tsx → /about
├── blog/
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/mijn-post
├── dashboard/
│ ├── page.tsx → /dashboard
│ └── settings/
│ └── page.tsx → /dashboard/settings
```
- **Geen react-router nodig!**
- **Nested routes** = nested folders
### Docentnotities
Tim legt het kernprincipe uit.
"Dit is het meest elegante aan Next.js. Wil je een `/about` pagina? Maak een `about` folder met een `page.tsx` erin. Klaar. Geen router configuratie, geen `<Route path='/about'>`. Gewoon: folder = route."
*Tim maakt live een nieuwe folder `about/page.tsx` aan.*
"En nesting werkt hetzelfde. Dashboard met een settings subpagina? `dashboard/settings/page.tsx`. Next.js snapt automatisch de structuur."
"Dit is waarom developers Next.js zo fijn vinden. Je ziet de folder structuur en je weet meteen welke pagina's je app heeft."
---
## Slide 9: Speciale Bestanden
### Op de Slide
- **`page.tsx`** — De pagina zelf (verplicht voor een route)
- **`layout.tsx`** — Wrapper om pagina's (header, footer, sidebar)
- **`loading.tsx`** — Loading state (automatisch getoond)
- **`error.tsx`** — Error boundary (vangt fouten op)
- **`not-found.tsx`** — 404 pagina
- **`route.ts`** — API endpoint (in plaats van page)
```
app/
├── layout.tsx ← wraps ALLES
├── page.tsx ← homepage
├── loading.tsx ← loading spinner
├── error.tsx ← error handler
├── not-found.tsx ← 404 pagina
└── blog/
├── layout.tsx ← wraps alleen /blog/*
└── page.tsx ← /blog pagina
```
### Docentnotities
Tim legt elk bestand uit.
"Next.js heeft speciale bestanden die automatisch iets doen. De belangrijkste: `page.tsx` — zonder die is er geen route. Dan `layout.tsx` — dat is je wrapper. Denk aan een template: header bovenaan, footer onderaan, en daartussen wisselt de content."
"Wat heel krachtig is: layouts nesten. Je root layout heeft misschien een navbar. Je blog layout voegt een sidebar toe. Alles composeert vanzelf."
"En dan heb je `loading.tsx` en `error.tsx`. Die zijn magisch. Als je pagina data laadt, toont Next.js automatisch je loading component. Geen `useState(true)` en `if (loading)` meer. Het framework doet het voor je."
---
## Slide 10: Layouts in Actie
### Op de Slide
```tsx
// src/app/layout.tsx — Root Layout
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Mijn App",
description: "Gebouwd met Next.js",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="nl">
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>{children}</main>
<footer>© 2025 NOVI</footer>
</body>
</html>
);
}
```
- **`children`** = de pagina die in de layout wordt gerenderd
- Layout blijft staan bij navigatie (geen re-render)
- **`Metadata`** type uit Next.js voor type-safe SEO
### Docentnotities
Tim toont hoe layout en page samenwerken.
"Kijk, de root layout wraps alles. Elke pagina in je app krijgt deze nav en footer. En het mooie: als je navigeert van Home naar About, wordt de layout NIET opnieuw gerenderd. Alleen de `children` — de page — wisselt."
"Let op de TypeScript: we importeren het `Metadata` type van Next.js. Dat zorgt ervoor dat je editor je helpt met de juiste properties. Titel, description, Open Graph — alles is getypt."
"Dit is een groot verschil met een gewone React app waar je hele component tree opnieuw rendert bij elke route change."
---
## Slide 11: Dynamic Routes
### Op de Slide
- **Dynamic segments** met vierkante haken: `[param]`
```
app/
└── blog/
└── [slug]/
└── page.tsx → /blog/mijn-eerste-post
→ /blog/nextjs-is-cool
→ /blog/wat-dan-ook
```
```tsx
// src/app/blog/[slug]/page.tsx
interface PageProps {
params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: PageProps) {
const { slug } = await params;
return <h1>Blog post: {slug}</h1>;
}
```
- **Meerdere params:** `/shop/[category]/[productId]`
- **Catch-all:** `[...slug]` → vangt alles op
### Docentnotities
Tim legt dynamic routes uit met een concreet voorbeeld.
"Stel je hebt een blog met 100 posts. Je gaat geen 100 folders aanmaken. In plaats daarvan maak je één folder met vierkante haken: `[slug]`. Dat is een variabele. Alles wat je in de URL typt wordt beschikbaar als parameter."
"Dus `/blog/mijn-eerste-post` — slug is `mijn-eerste-post`. `/blog/nextjs-is-cool` — slug is `nextjs-is-cool`. Eén page component handelt alle blog posts af."
"Let op de TypeScript typing: `params` is een Promise met een object. Dat is nieuw in Next.js 15 — je moet params await'en. En kijk: we definiëren een `PageProps` interface. Alles wat je in Les 4 geleerd hebt, gebruiken we hier."
---
## Slide 12: Route Groups
### Op de Slide
- **Route Groups** met ronde haken: `(naam)`
- Folder voor organisatie, maar NIET in de URL
```
src/app/
├── (marketing)/
│ ├── layout.tsx ← eigen layout voor marketing
│ ├── page.tsx → /
│ ├── about/
│ │ └── page.tsx → /about
│ └── pricing/
│ └── page.tsx → /pricing
├── (dashboard)/
│ ├── layout.tsx ← eigen layout voor dashboard
│ ├── dashboard/
│ │ └── page.tsx → /dashboard
│ └── settings/
│ └── page.tsx → /settings
```
- `(marketing)` en `(dashboard)` verschijnen NIET in de URL
- Elk group kan zijn eigen layout hebben
- Handig voor: verschillende layouts, code organisatie
### Docentnotities
Tim legt route groups uit als organisatie tool.
"Stel je hebt een marketing website en een dashboard in dezelfde app. De marketing site heeft een heel ander design dan het dashboard. Hoe los je dat op?"
"Route Groups. Je maakt een folder met ronde haken: `(marketing)` en `(dashboard)`. Het verschil met vierkante haken: ronde haken verschijnen NIET in de URL. Het is puur voor organisatie."
"Dus `(marketing)/about/page.tsx` wordt gewoon `/about`. Niet `/(marketing)/about`. En elk group kan zijn eigen layout hebben. Marketing met een hero en footer, dashboard met een sidebar en topbar. Allemaal in hetzelfde project."
"Dit is hoe grote Next.js apps georganiseerd worden. Je scheidt concerns zonder de URL structuur te beïnvloeden."
---
## Slide 13: Project Structuur Best Practices
### Op de Slide
```
src/
├── app/ ← Routes & pages
│ ├── (marketing)/
│ ├── (dashboard)/
│ └── api/
├── components/ ← Herbruikbare UI components
│ ├── ui/ ← Generieke components (Button, Card)
│ └── features/ ← Feature-specifiek (PollCard, VoteForm)
├── lib/ ← Utility functies & helpers
│ ├── utils.ts
│ └── api.ts
├── types/ ← TypeScript type definities
│ └── index.ts
└── middleware.ts ← Middleware (altijd in src root)
```
- **Regel 1:** `app/` is alleen voor routing — geen componenten
- **Regel 2:** Gedeelde components in `components/`
- **Regel 3:** Types in `types/` — herbruikbaar across het project
- **Regel 4:** Business logic in `lib/`
### Docentnotities
Tim deelt praktische structuur tips.
"Nu je weet hoe routing werkt, is de vraag: waar zet je de rest? Components, types, utility functies? Hier is de conventie die de meeste Next.js projecten volgen."
*Tim wijst naar de structuur.*
"De `app/` folder is ALLEEN voor routing. Pagina's, layouts, API routes. Geen losse components. Die gaan in `components/`. Onderscheid tussen generieke UI components — Button, Card, Modal — en feature-specifieke components — PollCard, VoteForm."
"Types in een `types/` folder. Zo kun je ze importeren vanuit elke plek in je project. En business logic — data fetching, formatters, validators — in `lib/`."
"Dit is geen harde regel van Next.js. Maar het is wel de conventie. En als je in een team werkt, weet iedereen meteen waar dingen staan."
---
## Slide 14: TypeScript in Next.js
### Op de Slide
- **Next.js is volledig getypt** — alles heeft types
```tsx
// Page props zijn getypt
interface PageProps {
params: Promise<{ id: string }>;
searchParams: Promise<{ query?: string; page?: string }>;
}
export default async function SearchPage({ params, searchParams }: PageProps) {
const { query, page } = await searchParams;
// ...
}
```
```tsx
// API Route met getypte request body
interface CreatePollBody {
question: string;
options: string[];
}
export async function POST(request: Request) {
const body: CreatePollBody = await request.json();
// TypeScript checkt nu of body.question en body.options bestaan
}
```
- **`Metadata`** type voor SEO
- **`NextRequest`** en **`NextResponse`** voor API/middleware
- Alles uit Les 4 (interfaces, union types) komt hier terug
### Docentnotities
Tim maakt de connectie met Les 4.
"Hier komt Les 4 echt samen. Alles in Next.js is getypt. Page props? Getypt. API request bodies? Getypt. Middleware? Getypt."
"Kijk naar dit voorbeeld. We definiëren een `PageProps` interface met `params` en `searchParams`. Beide zijn Promises — dat is een Next.js 15 patroon. SearchParams kan `query` en `page` bevatten, maar ze zijn optioneel — de vraagtekens. Dat is de optional syntax uit Les 4."
"En voor API routes: je definieert een interface voor de request body. `CreatePollBody` met `question` en `options`. Nu weet TypeScript precies wat er binnenkomt. Als je per ongeluk `body.titel` typt in plaats van `body.question`, krijg je direct een rode lijn."
"Dit is waarom we vorige week TypeScript hebben geleerd. Niet als losstaand iets, maar als fundamenteel onderdeel van hoe je Next.js apps bouwt."
---
## Slide 15: Server Components vs Client Components
### Op de Slide
- **Server Components** (standaard):
- Renderen op de server
- Geen JavaScript naar de browser
- Kunnen direct database/API aanroepen
- Kunnen async zijn
- ❌ Geen useState, useEffect, onClick
- **Client Components** (`"use client"`):
- Renderen in de browser
- Interactiviteit: useState, useEffect, event handlers
- ❌ Geen directe database/API server-side
```tsx
// Server Component (standaard) — async allowed!
export default async function ProductList() {
const res = await fetch("https://api.example.com/products");
const products: Product[] = await res.json();
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}
```
```tsx
// Client Component — interactiviteit
"use client";
import { useState } from "react";
export default function LikeButton() {
const [likes, setLikes] = useState<number>(0);
return <button onClick={() => setLikes(likes + 1)}> {likes}</button>;
}
```
### Docentnotities
Tim maakt het verschil heel duidelijk.
"Dit is misschien het belangrijkste concept in Next.js. Standaard is elk component een Server Component. Dat betekent: het draait op de server, niet in de browser. De browser krijgt alleen HTML — geen JavaScript."
"Waarom is dat goed? Performance. Je stuurt minder code naar de browser. En je kunt direct data fetchen — de server component kan async zijn. Gewoon `await fetch()` in je component."
"Maar: server components kunnen geen interactiviteit hebben. Geen useState, geen onClick. Daarvoor heb je Client Components nodig. Je zet `'use client'` bovenaan je bestand en dan werkt het zoals je gewend bent van React."
"De truc is: gebruik server components voor alles wat kan, en client components alleen waar je interactiviteit nodig hebt. De meeste code is server, en kleine interactieve stukjes zijn client."
"En kijk: `useState<number>(0)` — de TypeScript generic syntax uit Les 4!"
---
## Slide 16: Data Fetching in Server Components
### Op de Slide
- **Server Components kunnen direct data fetchen** — geen useEffect nodig
```tsx
// src/app/polls/page.tsx — Server Component
interface Poll {
id: string;
question: string;
options: string[];
votes: number[];
}
async function getPolls(): Promise<Poll[]> {
const res = await fetch("https://api.example.com/polls", {
next: { revalidate: 60 }, // Cache voor 60 seconden
});
return res.json();
}
export default async function PollsPage() {
const polls = await getPolls();
return (
<div>
<h1>Alle Polls</h1>
{polls.map((poll) => (
<PollCard key={poll.id} poll={poll} />
))}
</div>
);
}
```
- **`next: { revalidate: 60 }`** — cache + automatisch verversen
- **Geen useState, useEffect, loading state nodig!**
- Next.js toont automatisch `loading.tsx` tijdens het fetchen
### Docentnotities
Tim toont het verschil met klassieke React data fetching.
"In gewoon React fetch je data met useEffect. useState voor loading, useState voor error, useState voor data. Drie states voor één ding. In Next.js? Niet nodig."
"Een server component kan async zijn. Je schrijft gewoon `await fetch()`. De component wacht op de data en rendert het resultaat. En terwijl het wacht, toont Next.js automatisch je `loading.tsx`."
"En dat `revalidate: 60`? Dat is caching. De eerste keer fetcht Next.js de data. De volgende 60 seconden serveert het de cache. Na 60 seconden fetcht het opnieuw. Zero configuratie."
"Vergelijk dat met React: daar moet je React Query of SWR installeren, loading states beheren, caching instellen... Next.js doet het allemaal voor je."
---
## Slide 17: Server Actions
### Op de Slide
- **Server Actions** = functies die op de server draaien, aangeroepen vanuit forms
- Geen API route nodig voor form handling!
```tsx
// src/app/create/page.tsx
export default function CreatePollPage() {
async function createPoll(formData: FormData) {
"use server";
const question = formData.get("question") as string;
const options = formData.get("options") as string;
// Dit draait op de SERVER
console.log("Nieuwe poll:", question);
// Database insert, API call, etc.
}
return (
<form action={createPoll}>
<input name="question" placeholder="Stel je vraag..." required />
<input name="options" placeholder="Opties (komma-gescheiden)" required />
<button type="submit">Maak Poll</button>
</form>
);
}
```
- **`"use server"`** in de functie = draait op de server
- Geen `e.preventDefault()`, geen `fetch()` naar een API
- FormData wordt automatisch doorgestuurd
- Werkt ook zonder JavaScript in de browser!
### Docentnotities
Tim introduceert Server Actions als moderne form handling.
"Server Actions zijn relatief nieuw in Next.js en ze veranderen hoe je met formulieren werkt. In plaats van een onSubmit handler met fetch naar een API route, gebruik je een `action` op het form die direct een server functie aanroept."
"Kijk: `'use server'` in de functie body. Dat vertelt Next.js: dit draait op de server. De browser stuurt het form, de server verwerkt het. Geen API route nodig."
"Waarom is dit handig? Ten eerste: minder code. Geen API route bestand, geen fetch call, geen response handling. Ten tweede: het werkt zelfs zonder JavaScript in de browser — progressive enhancement."
"In de opdracht van vandaag gaan jullie nog gewone API routes gebruiken, want dat is belangrijk om te begrijpen. Maar bij het huiswerk mogen jullie Server Actions proberen als alternatief."
---
## Slide 18: Klassikaal Bouwen — QuickPoll App Part 1
### Op de Slide
- **Uitleg:** Tim codeert op het scherm, jullie volgen mee in je eigen project
- **Dit is niet solo werk** — we doen het SAMEN
- **Stap 0-3 vandaag:**
- Stap 0: Project Setup
- Stap 1: Layout & Navigatie
- Stap 2: Homepage — Polls Lijst
- Stap 3: API Route — GET Single Poll
- **Tempo:** Tim pauzeert regelmatig — "Heeft iedereen dit? Steek je hand op"
- **Cursor Tab:** Mag gebruiken (Tab completion), maar NIET Cmd+K (generator)
### Docentnotities
Tim start het klassikaal bouwen.
"Oké, nu gaan we bouwen. Dit keer anders dan vorige keer — we werken samen. Ik code op mijn scherm, jullie volgen mee. Als je achterblijft, steek je je hand op en wij wachten."
"Waarom samen? Omdat jullie vorig keer veel te veel alleen zaten, en dat was niet naar jullie voorkeur. Dit is beter voor iedereen."
"Cursor mag aan voor Tab completion — dat helpt je typen. Maar Cmd+K mag nog niet. We willen dat je het zelf begrijpt, niet dat Cursor het schrijft."
"Laten we beginnen."
---
## Slide 19: Stap 0 — Project Setup
### Op de Slide
- **Unzip starter** (of create-next-app)
```bash
npx create-next-app@latest quickpoll --typescript --tailwind --app --src-dir
cd quickpoll
npm run dev
```
- **Folder structuur klaarmaken:**
```
src/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── poll/
│ │ └── [id]/
│ │ └── page.tsx
│ └── api/
│ └── polls/
│ └── [id]/
│ └── route.ts
├── components/
│ └── PollCard.tsx
├── lib/
│ └── data.ts
├── types/
│ └── index.ts
└── middleware.ts
```
- **Types definiëren:**
```tsx
// src/types/index.ts
export interface Poll {
id: string;
question: string;
options: string[];
votes: number[];
}
```
- **Bezoek `localhost:3000`** — controleer dat het werkt
### Docentnotities
Tim doet setup live, klas volgt stap voor stap.
"Stap 0: we zetten het project op. Iedereen typt het create-next-app commando mee."
*Tim typt het commando, wacht tot iedereen het draait.*
"Terwijl dat installeert, maken we de folder structuur aan. Niet in code — gewoon de lege folders. Klik op app/, nieuwe folder 'poll'. Daarin '[id]'. Daarin 'page.tsx'. Vervolgens 'api/', daarin 'polls/', daarin '[id]/', daarin 'route.ts'."
*Tim maakt alles visueel aan.*
"Nu types. Maak `src/types/index.ts` aan. Dit is waar alle TypeScript interfaces leven."
*Tim typt de Poll interface.*
"Klaar? Open je browser op localhost:3000. Je moet de default Next.js pagina zien. Mooi — je project leeft."
---
## Slide 20: Stap 1 — Layout & Navigatie
### Op de Slide
- **Update root layout met navbar:**
```tsx
// src/app/layout.tsx
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "QuickPoll — Stem op alles",
description: "Democratie in je broekzak",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="nl">
<body className="bg-gray-50">
<nav className="bg-white border-b border-gray-200">
<div className="max-w-4xl mx-auto px-4 py-4 flex justify-between items-center">
<Link href="/" className="text-2xl font-bold text-blue-600">
QuickPoll
</Link>
<div className="space-x-4">
<Link href="/" className="text-gray-600 hover:text-gray-900">
Home
</Link>
</div>
</div>
</nav>
<main className="max-w-4xl mx-auto px-4 py-8">{children}</main>
</body>
</html>
);
}
```
- **Test:** Navigatie zichtbaar? Styling oke?
### Docentnotities
Tim bouwt de layout.
"Stap 1: layout. Dit is je wrapper — header, footer, sidebar — alles wat op elke pagina hetzelfde is."
*Tim typt de layout.*
"Let op: we gebruiken Tailwind classes voor styling. `bg-white`, `border-b`, `max-w-4xl` — allemaal standaard Tailwind. En we gebruiken `next/link` voor de navigatie — geen `<a>` tags."
"Metadata staat ook hier — Google gaat dat zien. Titel en description."
*Tim opent localhost:3000.*
"Zien jullie de navbar? Prima. Dan gaan we verder."
---
## Slide 21: Stap 2 — Homepage & Polls Lijst
### Op de Slide
- **In-memory data aanmaken:**
```tsx
// src/lib/data.ts
import { Poll } from "@/types";
export const polls: Poll[] = [
{
id: "1",
question: "Wat is je favoriete programming language?",
options: ["TypeScript", "Python", "Rust", "Go"],
votes: [15, 8, 6, 3],
},
{
id: "2",
question: "Voorkeur: Dark mode of Light mode?",
options: ["Dark", "Light"],
votes: [22, 8],
},
];
```
- **Homepage component:**
```tsx
// src/app/page.tsx
import Link from "next/link";
import { polls } from "@/lib/data";
export default function HomePage() {
return (
<div>
<h1 className="text-4xl font-bold mb-8">Alle Polls</h1>
<div className="grid gap-4">
{polls.map((poll) => (
<Link key={poll.id} href={`/poll/${poll.id}`}>
<div className="bg-white p-6 rounded-lg border border-gray-200 hover:shadow-lg transition">
<h2 className="text-xl font-semibold">{poll.question}</h2>
<p className="text-gray-500 text-sm mt-2">
{poll.options.length} opties {poll.votes.reduce((a, b) => a + b, 0)} stemmen
</p>
</div>
</Link>
))}
</div>
</div>
);
}
```
- **Test:** Zie je 2 poll cards? Kunnen je erop klikken?
### Docentnotities
Tim bouwt de homepage.
"Stap 2: we maken data en tonen die op de homepage."
*Tim maakt eerst data.ts.*
"In-memory array met poll objects. Dit is waar al onze data leeft. In de echte wereld zou dit een database zijn, maar voor vandaag: JavaScript array."
*Tim update page.tsx.*
"Homepage component — geen `'use client'`, dus het is een server component. We fetchen polls, we mappen erover, we renderen kaarten. Link wrapper maakt elke kaart klikbaar."
*Tim opent localhost:3000.*
"Mooi! Je ziet twee polls. Ze zijn klikbaar. Maar de poll detail pagina bestaat nog niet — je krijgt 404. Dat doen we straks."
---
## Slide 22: Stap 3 — API Route GET Single Poll
### Op de Slide
- **Dynamic API route voor 1 poll:**
```tsx
// src/app/api/polls/[id]/route.ts
import { NextResponse } from "next/server";
import { polls } from "@/lib/data";
interface RouteParams {
params: Promise<{ id: string }>;
}
export async function GET(
request: Request,
{ params }: RouteParams
): Promise<NextResponse> {
const { id } = await params;
const poll = polls.find((p) => p.id === id);
if (!poll) {
return NextResponse.json(
{ error: "Poll niet gevonden" },
{ status: 404 }
);
}
return NextResponse.json(poll);
}
```
- **Test:** Ga naar `/api/polls/1` — zie je JSON?
### Docentnotities
Tim bouwt de API route.
"Stap 3: API route voor één poll. Dit is belangrijk om goed te begrijpen."
*Tim typt de route.*
"Map erover die array, vind de poll met het gegeven ID. Niet gevonden? 404. Wel gevonden? Return JSON."
"Let op: `params` is een Promise in Next.js 15 — je moet het awaiten. Dit is nieuw!"
*Tim opent `/api/polls/1` in de browser.*
"Je ziet JSON terug. Mooi. Dit is je API. Nu kunnen client components dit aanroepen en stemmen vastleggen."
---
## Slide 23: Huiswerk & Volgende Les
### Op de Slide
- **Huiswerk vandaag (voor wie achterblijft):**
- Zorg dat stap 0-3 volledig werkt
- Test alle routes
- Zet je code op GitHub
- **Volgende les (Les 6) — Part 2:**
- Stap 4: Client-side VoteForm component
- Stap 5: POST /api/polls/[id]/vote
- Stap 6: Poll detail pagina /poll/[id]
- Stap 7: Middleware & loading states
- Blok 4 theorie: advanced features
- Deployment op Vercel
- **Preview:** Volgende keer maken we het interactief — stemmen opslaan, real-time updates
### Docentnotities
Tim vat samen en geeft huiswerk.
"Stap 0-3 zijn klaar. Je hebt een homepage met polls, en een API route die single polls teruggeeft."
"Huiswerk: maak het af als je nog achterblijft. Zet het op GitHub zodat we volgende les kunnen doorgaan."
"Volgende les is Part 2. Dan bouwen we de stem-functie — VoteForm component, POST route, alles interactief. We deployen ook op Vercel."
"Tot volgende les!"
---
## Slide 24: Afsluiting
### Op de Slide
- **Wat we vandaag gebouwd hebben:**
- Next.js project van nul
- App Router routing — folder = route
- Server Components en data fetching
- API routes met dynamic parameters
- TypeScript interfaces overal
- Tailwind CSS styling
- In-memory data management
- **Key moments van vandaag:**
- Layout wraps alles (geen re-render bij navigatie)
- Server components: gewoon async/await
- Route handlers: [id]/route.ts = dynamic API
- **Klaar voor Part 2:**
- Volgende les: client interactiviteit, voting, deployment
### Docentnotities
Tim sluit enthousiast af.
"Jongens, wat hebben we gebouwd vandaag! Een echte Next.js app met routing, API's, TypeScript — allemaal samen. Dit is wat developers doen."
"Je snapt nu hoe Next.js apps werken. Routing via folders, server components standaard, kleine stukjes client code waar je het nodig hebt."
"Volgende les: we maken het interactief. Dan stemmen mensen echt, en we zien live hoe Vercel deployment werkt."
"Goed gedaan vandaag! Tot volgende week!"