Next.js data fetching is een van de eerste dingen die ontwikkelaars in verwarring brengt na de overstap naar de App Router. Want wat het gedrag van je pagina bepaalt, is niet langer alleen waar en hoe je de data ophaalt, maar wanneer en hoe lang die data in de cache staat. Dezelfde fetch-aanroep kan een pagina volledig statisch maken, bij elke request opnieuw genereren of op vaste intervallen verversen, afhankelijk van een paar opties die je meegeeft. In dit artikel leg ik de verschillen tussen static rendering, SSR en ISR uit met concrete voorbeelden.
De basis van fetchen in Server Components
In de App Router zijn componenten standaard Server Components. Dat betekent dat je een component async kunt maken en er rechtstreeks await fetch(...) in kunt aanroepen. Geen aparte useEffect, state-beheer of laadvlag nodig; de data wordt op de server voorbereid en in de HTML naar de browser gestuurd.
// app/producten/page.tsx
export default async function ProductenPage() {
const res = await fetch('https://api.voorbeeld.com/producten');
const producten = await res.json();
return (
<ul>
{producten.map((p) => (
<li key={p.id}>{p.naam}</li>
))}
</ul>
);
}
Cruciaal punt: of deze code statisch of dynamisch draait, hangt af van de opties cache en next die je aan fetch meegeeft.
Static rendering: één keer bouwen, eindeloos serveren
Static rendering produceert de pagina eenmalig tijdens de build en serveert de resulterende HTML keer op keer. Het is ideaal voor inhoud die zelden verandert — blogartikelen, documentatie, productbeschrijvingen — omdat elke request zo snel als een CDN-bestand wordt beantwoord.
Een belangrijk detail: met Next.js 15 is het standaardgedrag van fetch veranderd. Requests worden nu standaard niet meer gecachet (in Next.js 14 was de standaard wél gecachet). Wil je statisch, gecachet gedrag, dan moet je dat expliciet inschakelen:
// Resultaat blijvend cachen -> statisch
const res = await fetch('https://api.voorbeeld.com/producten', {
cache: 'force-cache',
});
Bij routes met een dynamisch segment (bijv. app/blog/[slug]/page.tsx) geef je met generateStaticParams aan welke pagina's vooraf gegenereerd worden:
export async function generateStaticParams() {
const artikelen = await fetch('https://api.voorbeeld.com/artikelen').then((r) => r.json());
return artikelen.map((a) => ({ slug: a.slug }));
}
SSR: verse data bij elke request
Server-Side Rendering (SSR) produceert de pagina op de server bij elke binnenkomende request. Het is de juiste keuze voor gebruikersspecifieke dashboards, winkelmandjes, live prijzen of voortdurend veranderende data. Om dit in te schakelen zet je de cache volledig uit:
const res = await fetch('https://api.voorbeeld.com/prijzen', {
cache: 'no-store',
});
Hetzelfde effect bereik je op routesegmentniveau. Voeg je onderstaande regel bovenaan de pagina toe, dan wordt alle data op die route bij elke request opnieuw opgehaald:
export const dynamic = 'force-dynamic';
De route schakelt ook automatisch naar dynamisch zodra je cookies(), headers() of searchParams leest, omdat de uitvoer per gebruiker kan verschillen.
ISR: statische snelheid + periodieke versheid
Incremental Static Regeneration (ISR) slaat een brug tussen de snelheid van static rendering en de versheid van SSR. De pagina wordt statisch geserveerd, maar zodra het door jou ingestelde interval verstrijkt, wordt ze op de achtergrond opnieuw gegenereerd. Gebruikers krijgen dus altijd een snelle gecachete versie, en de inhoud werkt met een redelijke vertraging bij.
// Elke 60 seconden verversen
const res = await fetch('https://api.voorbeeld.com/producten', {
next: { revalidate: 60 },
});
Je kunt dit ook op segmentniveau definiëren:
export const revalidate = 60;
ISR is op maat gemaakt voor e-commerce-overzichten, nieuwsfeeds of dashboards die een paar keer per uur veranderen: zelfs bij een miljoen requests per minuut wordt de regeneratie maar één keer geactiveerd, wanneer het interval verstrijkt.
On-demand revalidatie: precies op tijd bijwerken
In plaats van een vast interval wil je de cache misschien legen wanneer de inhoud werkelijk verandert. Next.js biedt hiervoor twee methoden: per tag (revalidateTag) en per pad (revalidatePath). Eerst markeer je de fetch met een tag:
await fetch('https://api.voorbeeld.com/producten', {
next: { tags: ['producten'] },
});
Vervolgens maak je in een Server Action of Route Handler — bijvoorbeeld wanneer een product vanuit het beheerpaneel wordt bijgewerkt — die tag ongeldig:
import { revalidateTag } from 'next/cache';
revalidateTag('producten');
Zo blijft de pagina even snel als statisch, terwijl wijzigingen meteen worden weergegeven. Voor projecten met een CMS of beheerpaneel geeft dit veel fijnere controle dan tijdgebaseerde ISR.
De juiste strategie kiezen
- Static: zelden veranderende inhoud, hoogste prestaties.
cache: 'force-cache'. - ISR: frequente updates die niet direct hoeven te zijn.
next: { revalidate: N }. - SSR: gebruikersspecifieke data of data die elke seconde verandert.
cache: 'no-store'. - On-demand: door een gebeurtenis getriggerde updates.
tags+revalidateTag.
Een praktische tip: begin voor de meeste pagina's met ISR en maak niet alles force-dynamic tenzij het echt moet. Onnodige dynamiek verspilt het grootste voordeel dat Next.js je biedt — de cache.
Veelgestelde vragen
Waarom verschilt de fetch-cache tussen Next.js 14 en 15?
In Next.js 15 wordt fetch standaard niet gecachet; je schakelt caching expliciet in met cache: 'force-cache' of revalidate. In Next.js 14 was het andersom — de standaard was gecachet. Dit verschil controleren bij het upgraden van oudere projecten voorkomt onverwachte verrassingen.
Hoe haal ik data op binnen een Client Component?
Servercaching geldt niet in Client Components; je haalt data op de klassieke manier op, bijvoorbeeld met useEffect of bibliotheken als SWR / React Query. Waar mogelijk is data ophalen in een Server Component en die als prop doorgeven meestal efficiënter.
Kan ik revalidate en force-dynamic samen gebruiken?
Nee, die twee zijn met elkaar in conflict. force-dynamic rendert de pagina bij elke request en negeert de cache, dus het revalidate-interval heeft geen betekenis meer. Kies revalidate voor periodiek verversen, of force-dynamic voor volledige dynamiek.
Wil je een data-fetching- en cachingstrategie opzetten in je Next.js-project? Met het juiste rendermodel kan een site zowel snel als actueel zijn. Laten we er samen naar kijken: neem contact met me op.