19 november 2024

Server-Side Rendering versus Static Site Generation in Next.js

Elk Next.js-project komt op hetzelfde kruispunt: moet de HTML voor een bepaalde pagina eenmalig gegenereerd worden tijdens de build en als statisch bestand geserveerd worden, of moet de server die bij elk inkomend verzoek vers aanmaken? Het onderscheid tussen Static Site Generation (SSG) en Server-Side Rendering (SSR) klinkt als een implementatiedetail op laag niveau, maar het bepaalt hoe snel pagina's laden, hoe verouderd de data kan zijn, hoeveel infrastructuur een team nodig heeft en hoe soepel een project schaalt onder echt verkeer. Het verkeerde model kiezen heeft niet alleen gevolgen voor een benchmark — het kan betekenen dat gebruikers verouderde content zien, dat oorspronkelijke servers het begeven onder belasting, of dat een codebase workarounds opstapelt die na verloop van tijd groter worden.

Het eerlijke antwoord is dat geen van beide benaderingen universeel beter is, en Next.js ondersteunt bewust beide — samen met hybride patronen zoals Incremental Static Regeneration (ISR) — precies omdat verschillende pagina's binnen dezelfde applicatie echt verschillende vereisten hebben. Een marketinglandingspagina, een realtime koersticker en een gebruikersspecifiek dashboard hebben elk andere beperkingen qua versheid, personalisatie en prestaties, waardoor één renderingstrategie voor alle drie niet geschikt is. Dit artikel werkt de echte afwegingen door zodat de keuze een bewuste technische beslissing wordt in plaats van een standaard.

Wat is Server-Side Rendering in Next.js

Server-Side Rendering (SSR) in Next.js betekent dat de HTML van een pagina op de server gegenereerd wordt op het moment dat een gebruiker een verzoek doet — niet vooraf tijdens een buildstap, en niet in de browser via JavaScript. Elk inkomend verzoek zorgt ervoor dat Next.js de data-fetchinglogica van de pagina uitvoert, de volledige HTML-respons opbouwt en die terugstuurt naar de client. Dit staat in contrast met benaderingen waarbij de server simpelweg een vooraf gebouwd bestand terugstuurt dat eerder gegenereerd werd. In Next.js wordt SSR op paginaniveau ingeschakeld door een async-functie te exporteren met de naam getServerSideProps. Wanneer Next.js deze export detecteert, weet het dat statische generatie voor die pagina overgeslagen moet worden en de functie bij elk verzoek uitgevoerd moet worden.

Binnen getServerSideProps heb je toegang tot de volledige verzoekcontext — inclusief headers, cookies en URL-queryparameters — waardoor het mogelijk is om gepersonaliseerde of tijdgevoelige data op te halen voordat de pagina de gebruiker bereikt. De functie geeft een props-object terug dat Next.js doorgeeft aan de paginacomponent, net zoals bij statische generatie. Het belangrijkste verschil is timing: bij SSR gebeurt die data-fetch bij elk afzonderlijk verzoek, zodat de HTML die naar de browser gestuurd wordt altijd de huidige toestand weerspiegelt van de databron die je opvraagt.

Het praktische gevolg van dit model is dat SSR-pagina's nooit verouderd zijn, maar ook nooit gratis. Elk verzoek verbruikt serverrekenkracht en voegt latentie toe ten opzichte van het serveren van een vooraf gebouwd bestand vanaf een CDN-edge-node. Voor pagina's waar de data vaak verandert, afhangt van de ingelogde gebruiker, of iets moet weerspiegelen dat seconden geleden gebeurde, is die afweging vaak de moeite waard. Voor pagina's waar de inhoud stabiel is voor alle gebruikers en in de tijd, introduceert SSR overhead zonder zinvol voordeel — en dat is precies waar statische generatie de meer geschikte standaard wordt.

Wat is Static Site Generation in Next.js

Static Site Generation (SSG) is een renderingstrategie waarbij Next.js de volledige HTML voor een pagina genereert op buildtijd — voordat een gebruiker ooit een verzoek doet. Wanneer je next build uitvoert, verwerkt Next.js je data-fetchinglogica, rendert de componentboom naar statische HTML en schrijft het resultaat naar schijf. Die vooraf gebouwde bestanden worden vervolgens gedeployed naar een CDN of statische hostingomgeving, waar ze onmiddellijk geserveerd kunnen worden zonder enige serververwerking per verzoek.

Het primaire mechanisme voor SSG in Next.js is getStaticProps, een async-functie die geëxporteerd wordt vanuit een paginabestand en alleen tijdens de build wordt uitgevoerd. Het kan API's aanroepen, een database bevragen of van het bestandssysteem lezen — alles wat normaal server-side zou gebeuren — maar het resultaat wordt ingebakken in het HTML-bestand in plaats van op aanvraag berekend. Voor pagina's met dynamische routes (zoals blogberichten of productpagina's) werkt getStaticPaths samen met getStaticProps om Next.js te vertellen welke URL-paden vooraf gerenderd moeten worden, zodat elke variant van de pagina als eigen statisch bestand geproduceerd wordt tijdens de build.

Het praktische gevolg is dat SSG-pagina's vrijwel geen serverbelasting hebben tijdens runtime en doorgaans goed scoren op prestatiemetrieken, omdat de browser een volledig opgebouwd HTML-document rechtstreeks van een CDN-edge-node ontvangt. De afweging is veroudering: de inhoud weerspiegelt wat er in de databron aanwezig was toen de build gedraaid werd, niet noodzakelijk wat er nu staat. Next.js biedt Incremental Static Regeneration (ISR) als tussenweg — pagina's kunnen op de achtergrond opnieuw gevalideerd worden na een opgegeven interval — maar het kernpatroon van SSG blijft een buildtijdoperatie, waardoor het het meest geschikt is voor inhoud die zelden verandert of een korte vertraging tussen bronaanpassingen en live pagina's kan tolereren.

Image

Hoe SSR werkt op verzoektijd

Elke SSR-pagina in Next.js volgt dezelfde runtime-lus: de browser stuurt een verzoek, Next.js onderschept het op de server, haalt de vereiste data op uit een API of database, rendert de volledige HTML en stuurt die terug naar de client. De pagina arriveert vooraf ingevuld — geen client-side data-fetching vereist. De afweging is dat deze volledige cyclus afgerond moet zijn voordat de gebruiker iets ziet, wat betekent dat elk verzoek meetbare latentie toevoegt die schaalt met de responstijden van de databron.

Image

Hoe SSG werkt op buildtijd

Tijdens een static site generation build roept Next.js data-fetchingfuncties zoals getStaticProps eenmalig aan — op buildtijd, niet op verzoektijd. De resulterende HTML-bestanden worden naar schijf geschreven en naar een CDN gepusht. Wanneer een gebruiker een pagina opvraagt, serveert de CDN rechtstreeks een vooraf gerenderd bestand, zonder betrokkenheid van een oorspronkelijke server en zonder berekening per verzoek. De buildpipeline is de bottleneck, niet de server — en daarom laden SSG-pagina's snel maar weerspiegelen ze alleen de data die aanwezig was toen de build liep.

SSR vs SSG: Belangrijkste kenmerken vergeleken

Een zij-aan-zij vergelijking van SSR en SSG op de kenmerken die het meest van belang zijn bij het kiezen van een renderingstrategie in Next.js.

Server-Side Rendering (SSR)Static Site Generation (SSG)
Versheid van dataAltijd actueel — opgehaald bij elk verzoekMogelijk verouderd — data ligt vast op buildtijd
Time to first byte (TTFB)Trager — server moet elk verzoek verwerkenSneller — vooraf gebouwde HTML wordt onmiddellijk geserveerd
HostingvereistenVereist een Node.js-server of serverloze runtimeKan gehost worden op elke statische CDN of bestandshost
BuildtijdSnel — geen vooraf renderen van pagina's vereistTrager op schaal — elke pagina wordt gerenderd op buildtijd
SchaalbaarheidSchaalt met servercapaciteit; hogere infrastructuurkostenSchaalt eenvoudig via CDN; minimale belasting van de oorsprong
Ideale toepassingGebruikersdashboards, realtime feeds, beveiligde pagina'sMarketingsites, documentatie, blogs, productoverzichten

Prestaties en Core Web Vitals

Een van de meest meetbare verschillen tussen SSG en SSR ligt in de Time to First Byte (TTFB) — de tijd die de browser nodig heeft om de eerste byte van een paginarespons te ontvangen. Bij SSG is de HTML vooraf gebouwd en opgeslagen op een CDN-edge-node geografisch dicht bij de gebruiker, zodat die eerste byte doorgaans aankomt in milliseconden met één of twee cijfers. Bij SSR zorgt elk verzoek voor een nieuwe rendercyclus op de server — data ophalen, React-componenten uitvoeren en de uitvoer serialiseren — voordat iets teruggestuurd wordt naar de browser, wat latentie toevoegt die onder belasting oploopt.

Dit TTFB-verschil heeft een direct downstream-effect op de Core Web Vitals, met name op de Largest Contentful Paint (LCP) — de metriek die Google gebruikt om te meten hoe snel de hoofdinhoud van een pagina zichtbaar wordt. Omdat de LCP-timing begint op het moment dat een navigatie start, betekent een tragere TTFB vrijwel per definitie een tragere LCP, ongeacht hoe efficiënt de client-side JavaScript daarna draait. Voor pagina's waar zowel zoekmachinerangschikking als gebruikersbehoud afhangen van waargenomen laadsnelheid, heeft SSG een structureel voordeel dat SSR met runtime-optimalisaties alleen niet kan goedmaken.

De belangrijke kanttekening is dat dit voordeel alleen geldt wanneer de inhoud een zekere mate van veroudering kan tolereren. Een SSG-pagina weerspiegelt de toestand van de data op het moment van de laatste build, dus als de onderliggende inhoud vaak verandert, kunnen gebruikers verouderde informatie zien totdat de volgende build draait en de CDN-cache vernieuwd wordt. SSR garandeert daarentegen versheid bij elk verzoek — wat de juiste afweging is voor dashboards, gepersonaliseerde pagina's of inhoud waar correctheid in realtime zwaarder weegt dan de extra latentie. De prestatievraag is dan ook nooit puur technisch: ze vereist inzicht in het aanvaardbare verouderingsvenster voor een bepaalde pagina.

Render vers. Of render eenmalig.

Wanneer kies je voor SSR

Server-side rendering rechtvaardigt zijn plaats wanneer de inhoud van een pagina fundamenteel gebonden is aan de identiteit van de gebruiker die het verzoek doet. Dashboards, accountinstellingen, bestelgeschiedenispagina's en gepersonaliseerde feeds vallen allemaal in deze categorie: het juiste antwoord kan pas berekend worden zodra de server weet wie er vraagt en wanneer ze vragen. Een statisch gegenereerde versie van zo'n pagina zou ofwel de data van de verkeerde gebruiker tonen, ofwel zoveel client-side fetching vereisen dat de SSG-aanpak geen enkel voordeel meer biedt — je doet dan gewoon SSR met extra stappen.

Realtime datafeeds zijn een andere duidelijke toepassing voor SSR. Koerstickers, live sportscores, voorraaднiveaus en vluchtstatusboards moeten de exacte databasetoestand weerspiegelen op het moment van elk verzoek. Bij SSG kan een site op zijn best opnieuw valideren via een interval via Incremental Static Regeneration (ISR), maar zelfs een revalidatievenster van één minuut is onaanvaardbaar voor data die per seconde verandert. SSR elimineert die veroudering volledig, omdat Next.js getServerSideProps vers aanroept bij elk verzoek, waardoor de respons data bevat die nauwkeurig is tot op milliseconden van het laden van de pagina.

Een subtielere maar even geldige reden om voor SSR te kiezen is wanneer de HTML van de pagina moet variëren op basis van signalen op verzoektijd die onzichtbaar zijn voor een statische build: cookies, authenticatieheaders, geografische locatie opgelost server-side, of A/B-testingtoewijzingen. Deze signalen bestaan simpelweg niet op buildtijd, dus SSG kan ze niet verwerken in de gerenderde uitvoer. SSR verwerkt ze allemaal op een natuurlijke manier, omdat de server volledige toegang heeft tot het inkomende HTTP-verzoek voordat hij ook maar één regel markup rendert.

Wanneer kies je voor SSG

Static Site Generation is de juiste standaard voor inhoud die niet verandert op basis van wie er om vraagt of wanneer. Marketingsites, bedrijfsblogs, documentatieportalen en productcatalogi passen allemaal perfect in dit profiel: de inhoud wordt vooraf geschreven, wijzigingen zijn gepland en zeldzaam, en elke bezoeker moet dezelfde uitvoer zien. Wanneer Next.js deze pagina's vooraf rendert op buildtijd, kunnen de resulterende HTML-bestanden verspreid worden over een CDN en in milliseconden geserveerd worden — zonder dat één Node.js-proces per verzoek actief moet worden.

Het argument voor lagere infrastructuurkosten bij SSG is reëel en de moeite waard om duidelijk te stellen. Een statisch gegenereerde site onder zwaar verkeer legt geen extra rekenbelasting op — het CDN absorbeert de load zonder dat applicatieservers hoeven op te schalen. Dit maakt SSG bijzonder aantrekkelijk voor inhoud met onvoorspelbare verkeerspieken, zoals een blogbericht dat opgepikt wordt door een populaire nieuwsbrief of een productpagina die gelinkt wordt vanuit een persartikel. De site hoeft niet ingericht te worden voor worst-case gelijktijdigheid omdat er geen runtime-rekenkracht te voorzien valt.

SSG werkt ook goed samen met inhoud die een voorspelbaar updateschema volgt. Als een documentatiesite nieuwe pagina's uitbrengt met elke productreleases, of een nieuwssite zijn archief nachtelijks vernieuwt, houdt Incremental Static Regeneration (ISR) — het mechanisme van Next.js om vooraf gebouwde pagina's op een tijdsgebaseerd interval opnieuw te valideren — het statische model intact terwijl inhoud redelijk actueel kan blijven zonder volledige rebuilds. Het sleutelcriterium is of veroudering tolereerbaar is voor minuten of uren: als het antwoord ja is, levert SSG met ISR bijna altijd betere prestaties en eenvoudigere werking dan een volledig server-gerenderd alternatief.

Image

ISR: De middenweg

Incremental Static Regeneration bevindt zich tussen SSG en SSR op een manier die de kernspanning tussen versheid en prestaties oplost. Pagina's worden geserveerd als statische HTML — snel, cacheerbaar, goedkoop te leveren — maar Next.js hervalideert ze op de achtergrond op een configureerbaar tijdsinterval. Wanneer het interval verloopt en een nieuw verzoek binnenkomt, herbouwt Next.js die pagina stilletjes en wisselt de bijgewerkte versie in. Teams krijgen de meeste snelheidsvoordelen van SSG zonder te wachten op een volledige site-rebuild telkens wanneer inhoud verandert.

Veelgemaakte fouten om te vermijden

De meest voorkomende fout die teams maken is standaard SSR voor elke route kiezen als voorzorgsmaatregel, met de redenering dat server-gerenderde HTML altijd verser en dus altijd veiliger is. In de praktijk legt deze aanpak onnodige serverbelasting op en verhoogt het de responslatentie voor pagina's waarvan de inhoud zelden of nooit verandert. Een productoverzichtspagina die eenmaal per dag bijgewerkt wordt, heeft geen voordeel bij regeneratie bij elk verzoek — het heeft er alleen last van. SSR als universele terugval behandelen is een teken dat de renderingsbeslissing nooit bewust genomen is.

Een nauw verwante vergissing is het volledig negeren van Incremental Static Regeneration (ISR). Veel teams werken alsof Next.js alleen een binaire keuze biedt tussen volledig statisch en volledig server-gerenderd, terwijl ISR precies bestaat om de middenweg te bewandelen: inhoud die grotendeels stabiel is maar wel updates moet weerspiegelen binnen een voorspelbaar venster. Pagina's gebouwd met ISR kunnen geserveerd worden vanuit een CDN met bijna-statische prestaties terwijl ze op de achtergrond hervalideren op een gedefinieerd interval. ISR overslaan dwingt ontwikkelaars tot een onnodige afweging die het framework specifiek ontworpen is om te elimineren.

Ten slotte stapelen applicaties vaak inconsistente renderingsstrategieën per route op zonder gedocumenteerde redenering — het ene teamlid grijpt naar getServerSideProps, een ander gebruikt getStaticProps, en een derde laat een route als een client-only shell, allemaal zonder overleg. Na verloop van tijd leidt dit tot een codebase waarbij het renderingsgedrag van elke pagina onvoorspelbaar en moeilijk te auditen is. De praktische oplossing is eenvoudig: leg per route een korte beslissingsnotitie vast, al is het maar een commentaar dat uitlegt waarom die renderingsmodus gekozen werd. Expliciete redenering is goedkoop om te schrijven en duur om zes maanden later te reconstrueren.

SSR en SSG zijn geen concurrerende filosofieën — het zijn complementaire renderingsstrategieën die Next.js bewust binnen hetzelfde framework plaatst zodat teams elke strategie kunnen toepassen waar ze echt zinvol is. De praktische beslissing draait bij elke route om twee vragen: hoe vaak verandert de onderliggende data, en hoeveel verkeer zal die route absorberen? Pagina's waar inhoud stabiel is en prestaties het meest tellen, horen bij SSG; pagina's waar data de huidige toestand van de wereld op verzoektijd moet weerspiegelen, horen bij SSR. Next.js dwingt geen globale keuze af — een enkele applicatie kan en moet vaak beide gebruiken, route per route, met Incremental Static Regeneration beschikbaar als middenweg wanneer geen van beide extremen goed past. Teams die dit model internaliseren, stoppen met vragen "welke moeten we gebruiken" en beginnen de productievere vraag te stellen: "wat heeft deze specifieke route eigenlijk nodig?"