Uitleg — Server vs Client Components
💡 Simpele uitleg
- Server Component — draait op de server. De browser krijgt alleen HTML. Kan direct data ophalen, heeft geen
useStateofonClick. - Client Component — draait in de browser. Kan
useState,useEffecten event handlers gebruiken. Voeg'use client'bovenaan toe.
| Server Component | Client Component | |
|---|---|---|
| Waar draait het? | Server | Browser |
| Directive nodig? | Nee (standaard) | 'use client' bovenaan |
useState / useEffect |
❌ Niet mogelijk | ✅ Ja |
| Event handlers (onClick) | ❌ Niet mogelijk | ✅ Ja |
| Direct data ophalen | ✅ Ja (async/await) | Via useEffect |
| Browser APIs (localStorage) | ❌ Niet mogelijk | ✅ Ja |
💡 Vuistregel
Begin altijd met een Server Component. Voeg 'use client' alleen toe als je state, events of browser-APIs nodig hebt.
Uitleg — Hoe ziet een Server Component eruit?
Een Server Component is een gewone async functie — geen extra code nodig. Je kunt direct await gebruiken om data op te halen.
// app/workouts/page.js
// Geen 'use client' → dit is automatisch een Server Component
async function getWorkouts() {
const res = await fetch('http://localhost:3000/api/workouts');
return res.json();
}
export default async function WorkoutsPage() {
const workouts = await getWorkouts(); // ← direct awaiten, geen useEffect nodig
return (
<main>
<h1>Mijn Workouts</h1>
<ul>
{workouts.map((workout) => (
<li key={workout._id}>
{workout.title} — {workout.reps} reps @ {workout.load}kg
</li>
))}
</ul>
</main>
);
}
Uitleg — Hoe ziet een Client Component eruit?
Zodra je useState, useEffect of event handlers (zoals onClick) nodig hebt, zet je 'use client' als allereerste regel bovenaan het bestand.
// app/components/Voorbeeld.js
'use client'; // ← eerste regel, dit maakt het een Client Component
import { useState } from 'react';
export default function Voorbeeld() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
⚠️ Veelgemaakte fout
Als je useState of onClick gebruikt zonder 'use client', krijg je een foutmelding:
"You're importing a component that needs useState. It only works in a Client Component..."
Oplossing: Voeg 'use client' toe als eerste regel van het bestand.
Uitleg — Wanneer gebruik je wat?
Server Component als je:
- Data ophaalt van een API of database
- Geen interactie nodig hebt (geen knoppen, forms)
- Pagina's of layouts bouwt
Client Component als je:
useStateofuseReducernodig hebtuseEffectgebruikt- Event handlers hebt (onClick, onChange, onSubmit)
- Browser APIs gebruikt (localStorage, window)
Uitleg — Server en Client samen gebruiken
Een Server Component kan een Client Component renderen. Zo houd je data fetching op de server en zet je alleen het interactieve deel als Client Component.
// app/workouts/page.js ← Server Component
import AddWorkoutForm from '../components/AddWorkoutForm'; // ← Client Component
export default async function WorkoutsPage() {
// data fetching op de server...
return (
<main>
<AddWorkoutForm /> {/* Client Component in Server Component: ✅ */}
{/* rest van de pagina */}
</main>
);
}
Stap 1 — Maak het formulier component aan
Maak een nieuwe map components aan in app/ en maak daarin een nieuw bestand:
app/
└── components/
└── AddWorkoutForm.js ← nieuw bestand aanmaken
Zet deze code in app/components/AddWorkoutForm.js:
// app/components/AddWorkoutForm.js
'use client';
import { useState } from 'react';
export default function AddWorkoutForm() {
const [title, setTitle] = useState('');
const [reps, setReps] = useState('');
const [load, setLoad] = useState('');
const [error, setError] = useState(null);
async function handleSubmit(e) {
e.preventDefault();
setError(null);
const workout = { title, reps: Number(reps), load: Number(load) };
const res = await fetch('/api/workouts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(workout),
});
if (!res.ok) {
const data = await res.json();
setError(data.error);
return;
}
// Reset form
setTitle('');
setReps('');
setLoad('');
}
return (
<form onSubmit={handleSubmit}>
<h3>Workout Toevoegen</h3>
<input
type="text"
placeholder="Oefening (bijv. Push Day)"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<input
type="number"
placeholder="Reps"
value={reps}
onChange={(e) => setReps(e.target.value)}
/>
<input
type="number"
placeholder="Gewicht (kg)"
value={load}
onChange={(e) => setLoad(e.target.value)}
/>
<button type="submit">Toevoegen</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}
✅ Wat je nu hebt
Een formulier klaar voor gebruik. Het stuurt straks POST requests naar /api/workouts — die route bouw je in de API Routes stap.
Checklist
✅ Check of je hebt:
- Verschil tussen Server en Client Components begrepen
app/components/AddWorkoutForm.jsaangemaakt met'use client'- Formulier zichtbaar op de workouts pagina
- Geen errors in de console over ontbrekende
'use client'
Volgende Stap
Components begrijpen we nu. Tijd om echte data op te halen!
Bouw eerst de backend endpoints voordat je data ophaalt