c’t 07/2026
Wat is er waar van IT-mythes?
Cover van
Cover voor Van HTML tot serverside rendering: architecturen van web-apps

Van HTML tot serverside rendering: architecturen van web-apps

Multi-page- of single-page-app, serverside-rendering of static site generation? Als je de sterke en zwakke punten van de verschillende webarchitecturen kent, neem je weloverwogen beslissingen bij je volgende project.

Van statische pagina’s naar complexe webarchitecturen

In 1989, bij de geboorte van het World Wide Web, was de wereld nog overzichtelijk: een browser doet een HTTP-verzoek, een server levert een statisch document. Dat eenvoudige request-response-model heeft de webontwikkeling decennialang bepaald – en wordt ook vandaag de dag nog steeds gebruikt.

Front-end-ontwikkelaars hebben er in de loop der jaren veel nieuwe concepten op gebouwd om webapplicaties steeds persoonlijker, dynamischer en mooier te maken. Elke generatie ontwikkelaars erfde de keuzes van de vorige – ook de twijfelachtige. Zo werd juist de programmeertaal JavaScript, die in tien dagen in elkaar was geknutseld, een kernelement van de hele webstack.

Als je vandaag de dag een nieuwe webapplicatie ontwerpt, moet je de voordelen en de problemen kennen die de ontstane architecturen met zich meebrengen. Want geen van de architecturen werd volledig verdrongen of vervangen door de opvolgers – integendeel: ontwikkelaars hebben vandaag de dag het volledige repertoire aan architecturen uit bijna 40 jaar webgeschiedenis tot hun beschikking.

In het begin was er GET

Toen Tim Berners-Lee het web uitvond, had hij de uitwisseling van statische documenten in gedachten: wie iets nieuws wilde zien of gegevens wilde overdragen, stuurde een HTTP-verzoek naar de server. Die gaf voor elk verzoek een nieuw document terug.

Het web veranderde al snel in met formulieren doorspekte Rich Internet Applications, die meer waren dan statische documenten. Op de server stelden Perl, PHP, Java of Ruby op verzoek documenten samen. Het hierboven beschreven request-response-model bleef daarbij echter de standaard.

Tot ver na de dotcom-boom was de interactiviteit van web-apps de exclusieve taak van een server. Het begin van het einde van dat principe luidde Brendan Eich in 1995 in, toen hij haastig een middelmatige programmeertaal genaamd JavaScript in elkaar knutselde. JavaScript was eigenlijk bedoeld als aanvulling op Java-applets om ook scriptkiddies een programmeerbaar browserspeeltje te geven.

Halverwege de jaren 2000 zou dat JavaScript, dat in de browser draait en in de HTML-structuur kan ingrijpen, echter zelf dé tool worden van het Write once, run anywhere-paradigma, waarmee Sun Microsystems eigenlijk reclame had gemaakt voor Java.

Lees dit artikel verder

Lees over tech-trends en achtergronden, nieuwe apparatuur, software en toepassingen voor professioneel gebruik. Met c’t heb je altijd de juiste tech-informatie. Word abonnee en lees onbeperkt alle artikelen.
Bekijk abonnementen Al abonnee? Log in

Ontwikkelaars bliezen de HTML-documenten die door de servers werden opgehaald nieuw leven in met een flinke dosis JavaScript. Door animaties en het dynamisch herladen van inhoud ging de gebruikerservaring van web-apps steeds verder weg van die van een kaartjesautomaat en leek het steeds meer op native apps.

Maar met elke stap die de webontwikkelaars van toen zetten, werd de volgende stap alleen maar moeilijker.

Twee werelden

Een fundamenteel probleem van webapplicaties is dat ze tegelijkertijd in twee werelden bestaan. Een deel van hun logica zit op de server, het andere deel in de browser.

Op de server gelden de programmeertaal en de infrastructuur van je keuze. De browser biedt naast HTML, CSS en JavaScript hooguit nog WebAssembly als alternatief. Niet alleen zijn de technieken aan beide kanten meestal heel verschillend, maar ook wat ontwikkelaars ermee kunnen bereiken. Een Java-back-end kan niet zomaar de venstergrootte van een browser bepalen en het front-end-JavaScript zou idealiter geen directe toegang tot een database moeten hebben.

 Elke niet-triviale functie raakt echter potentieel beide werelden, wat de implementatie tot een echte uitdaging maakt. Voor de ontwikkeling van een groter project betekent dit dat verantwoordelijkheden moeten worden verdeeld en dat front- en back-endteams moeten worden gecoördineerd.

Terug naar de toekomst

In de vroege jaren 2010, toen dat probleem acuut werd, vond er in het front-end bovendien een explosie van complexiteit plaats. De smartphonerevolutie maakte webdesign exponentieel ingewikkelder omdat er veel meer schermformaten en sets met apparaatfuncties bijkwamen waar rekening mee moest worden gehouden.

Tegelijkertijd tilden de bij HTML5 geïntroduceerde browser-API’s de mogelijkheden van JavaScript naar een ongekend niveau. Steeds meer logica verhuisde naar het front-end. Maar dat betekende tegelijkertijd steeds meer coördinatie-inspanning met het back-end.

In die tijd herinnerden de ontwikkelaars zich hoe eenvoudig de wereld ooit was: één van de twee werelden – het back-end – bevatte alle relevante logica. Zou de oplossing voor alle problemen misschien niet kunnen liggen in een terugkeer naar het verleden?

In die tijd bloeiden twee nieuwe architecturen op, die deze heroriëntatie op heel verschillende manieren interpreteren: Static Site Generation (SSG) en Single-Page-applicaties (SPA’s).

Bij de eerste genereert een buildproces statische HTML-bestanden, die een relatief eenvoudige webserver alleen nog maar hoeft te leveren. SSG komt heel dicht in de buurt van de oorspronkelijke WWW-visie omdat het slechts een set van gekoppelde documenten genereert.

De server kan tijdens het genereren van de pagina’s ook complexe processen afhandelen, bijvoorbeeld zoekindexen ontwerpen. Daarom is SSG een serieus alternatief voor klassieke contentmanagementsystemen – maar alleen als het gaat om inhoud die maar af en toe wordt bijgewerkt.

Grote sprongen op het gebied van interactiviteit aan de clientzijde maak je met SSG echter niet.

De browser bepaalt de koers

Single-Page-Applications (SPA) zetten de architectuur van de vroege jaren 2000 op zijn kop. De focus ligt op het front-end en de server heeft maar twee taken: een API (meestal een REST-API) aanbieden voor gegevensuitwisseling en voor het eerste verzoek een leeg HTML-raamwerk terugsturen.

Dat werkt met een JavaScript-blob die alles bevat om in het front-end vanuit het niets een gebruikersinterface op te bouwen. De details worden meestal verzorgd door een van de front-endframeworks zoals React, Vue en Angular. Later kwam daar nog Svelte bij.

Single-page-applicaties hebben echter ook hun eigen nadelen. Ten eerste sturen ze een lege HTML-pagina en een script naar de client. Die vraagt even later de weer te geven gegevens apart op via JSON. Op die manier win je geen medaille qua prestaties.

Ten tweede hadden zoekmachines van oudsher een moeilijke relatie met single-page-applicaties, want een leeg raamwerk levert voor een crawler niet veel op.

Ten derde maakt die architectuur JavaScript of afgeleiden zoals TypeScript tot de dominante programmeertaal in het project.

Front-endtechnologie wordt de primaire lens waardoor alle beslissingen bekeken worden. De taal die ooit bedoeld was voor formuliervalidatie in een browser, regeert nu de hele stack. En dat kan voor data-intensieve, veiligheidskritieke of sterk parallelle back-endeisen simpelweg de verkeerde lens zijn.

Server en browser synchroniseren

Wat doe je dan als je een SPA wilt, maar de nadelen ervan niet kunt accepteren? De front-endscene heeft ook voor dat geval een oplossing bedacht: Server Side Rendering (SSR). In plaats van zoals vroeger aparte serverside templates te schrijven, gebruik je dezelfde JavaScript-code die ook in de browser draait, en voer je die uit op de server (isomorf JavaScript).

SSR werd mogelijk gemaakt door de runtime-omgeving Node.js. Die maakte JavaScript voor het eerst bruikbaar op een server. Pas daardoor kon dezelfde code, die in de browser een React- of Vue-component weergeeft, op de server worden uitgevoerd zonder dat je die voor een andere runtime-omgeving hoefde te herschrijven.

Server-Side Rendering wordt uitgevoerd door een server of een buildproces dat de JavaScript-code uitvoert die oorspronkelijk voor de browser bedoeld was, en daaruit statische HTML genereert in plaats van dynamische UI-updates. Dat is mogelijk omdat HTML uiteindelijk een serialisatie is van het Document Object Model (DOM) – precies die gegevensstructuur waarop clientside JavaScript werkt.

Hoe een eenvoudige React-component kan worden omgezet in zowel een interactieve UI als statische HTML, moet zelfs voor leken duidelijk zijn:

export function DisplayInteger( { input }) {
    if (!Number.isInteger(input)) {
        input = 0;
    }
    return <div>{input}</div>;
}

Die component produceert een <div>-element met daarin een geheel getal. Als de input-parameter niet met een geheel getal gevuld wordt, stelt de code standaard 0 in.

De pseudo-XML-syntaxis die frameworks zoals React gebruiken om componenten te beschrijven, heet JSX en is een uitbreiding van de normale JavaScript- en TypeScript-syntaxis. Omdat JSX geen directe HTML- of DOM-weergave is, maar een abstractie daarboven, kan de uitgangstoestand van een component worden geserialiseerd tot een HTML-string.

Voer je dat proces uit voor een hele boomstructuur van geneste componenten, dan ontstaat er een compleet HTML-document. Dat stuurt de server naar de browser – de gebruiker ziet de inhoud meteen, zonder te hoeven wachten tot JavaScript uitgevoerd is.

Vervolgens werkt het webframework op de achtergrond verder en ‘neemt’ de reeds aanwezige HTML over: het koppelt de statische elementen aan de bijbehorende JavaScript-componenten en maakt ze interactief.

Dat proces heet hydration: de ‘droge’ HTML wordt als het ware tot leven gewekt.

Problemen met twee werelden blijven bestaan

Daarmee zijn de SPA-problemen opgelost, maar ook SSR heeft zijn nadelen. Zo gaat er bij de serialisatie van DOM naar HTML informatie verloren. Maar bovenal blijft het fundamentele probleem bestaan dat de browser- en serverwerelden noodzakelijkerwijs heel verschillend zijn.

Code die slechts in één van beide werelden kan overleven en daarmee de hydration voor grotere problemen stelt, is dus snel geschreven:

export function DisplayInteger( { input }) {
    if (!Number.isInteger(input)) {
        input = window.innerWidth; // <– !!
    }
    return <div>{input}</div>;
}

De breedte van het browservenster die in window.innerWidth staat, is informatie die de server niet kent. Het is dus in principe onmogelijk om client en server van identieke inputs te voorzien en daarmee semantisch gelijkwaardige outputs te produceren.

Dit voorbeeld is een triviaal probleem. Maar het illustreert wel de onderliggende uitdaging van serverside-rendering in de context van moderne front-endontwikkeling: JavaScript-code werkt alleen op een client en server hetzelfde als hij onder alle denkbare omstandigheden gegarandeerd hetzelfde resultaat oplevert.

Voor deterministisch-functionele UI-componenten werkt dat misschien prima – maar lang niet voor alles. Componenten waarvan de input uitsluitend aan de clientzijde beschikbaar is, kunnen simpelweg niet aan de serverzijde weergegeven worden: als je de resultaten van een browsergebaseerde geolocatie-API wilt weergeven, heb je op de server niets in handen.

Ook leidt dezelfde input, afhankelijk van de omgeving, niet altijd op dezelfde manier naar het doel. Navigeren in de browser zorgt naast een DOM-update ook voor diverse neveneffecten, zoals het bijwerken van de geschiedenis. De server hoeft alleen maar een nieuw HTML-document te genereren.

En sommige logica hoort simpelweg alleen thuis op een server: secrets, tokens en schrijftoegang tot databases horen niet thuis in een browser.

De grotere frameworks – Angular, React, Vue en Svelte – hebben op de behoefte aan oplossingen voor serverside-rendering gereageerd met zogenaamde fullstack-oplossingen. Angular komt met een optionele SSR-module, die heel eenvoudig te activeren is en naadloos in het totaalconcept van Angular past.

React, hoewel in de kern zelf slechts een UI-bibliotheek, raadt al langer het gebruik van fullstack-frameworks zoals Next.js aan en beschouwt zichzelf dus als slechts een module in een grotere context.

Vue is net als React een UI-bibliotheek en heeft, eveneens net als React, met Nuxt een standaard fullstack-framework. De verhouding tussen SvelteKit en Svelte is dezelfde als die tussen React en Next.js en tussen Vue en Nuxt.

De verwantschap met de SPA-frameworks waarop ze expliciet voortbouwen is duidelijk, terwijl het servergedeelte vooral opvalt door de achteraf toegevoegde uitbreiding.

Concepten en eisen die voortkomen uit het front-end vormen de lens waardoor ontwikkelaars hun projecten bekijken. Daar hoort niet in de laatste plaats bij dat JavaScript (plus afgeleiden zoals TypeScript) de programmeertaal bij uitstek blijft.

Bij React moeten ontwikkelaars onderscheid maken tussen server- en clientcomponenten, maar het zijn tenminste allebei React-componenten. Bij Angular definieer je, indien nodig, expliciet een serverroute, maar je blijft tenminste in het vertrouwde Angular-nest zitten.

Objectief vast te stellen kwaliteitsverschillen tussen al die frameworks zijn marginaal en met geen van de genoemde tools kun je iets bereiken dat met de andere niet net zo snel en performant haalbaar zou zijn.

Als je nog niet bekend bent met een van de genoemde frameworks, kun je in principe makkelijk kiezen: ze doen grofweg allemaal hetzelfde op ongeveer dezelfde manier. Ze bieden zelfs allemaal een SSG-optie!

Soms volstaan statische pagina’s.

Overigens: als je nu met één van die zwaargewichten aan een nieuw project wilt beginnen, moet je eerst bedenken of het uitvoeren van client-JavaScript op een server een oplossing is voor een probleem dat je daadwerkelijk hebt.

De twee uitdagingen die door serverside-rendering werden opgelost en pas relevant werden door single-page-apps, waren:

1. Prestatieoptimalisatie voor het eerste laadproces

2. Zinvolle HTML-inhoud (voornamelijk voor zoekmachines) tijdens het eerste laadproces

In de wereld van webapplicaties zijn er veel scenario’s denkbaar waarin die twee problemen helemaal niet kunnen voorkomen en dus ook geen oplossing behoeven. Zo is het ook in 2026 zonder meer mogelijk om een ouderwetse request-response-app te ontwikkelen.

Of het nu PHP, Python of Java is: op elk relevant platform zijn er volwassen frameworks waarmee webapplicaties mogelijk zijn zoals onze voorvaderen die maakten. Dergelijke applicaties worden, in tegenstelling tot single-page-apps, ook wel Multi-Page-Apps (MPA) genoemd.

Net als vroeger leveren ze HTML-pagina’s af, die indien nodig kunnen worden aangekleed met wat JavaScript of zelfs moderne browsertechnologie zoals view-transitions. Die ouderwetse aanpak uit de vroege jaren 2000 wint geen innovatieprijs, maar kan afhankelijk van het project een optimaal compromis zijn – Boring Technology op zijn best. En omdat er geen concessies aan het browserplatform nodig zijn, is er in plaats van een JavaScript-verplichting een vrije keuze van programmeertaal!

Het tegenovergestelde van de klassieke MPA, de klassieke SPA, is ook een optie. Voor veel toepassingen zijn zoekmachinevriendelijkheid of het optimaliseren van het eerste laadproces niet relevant en is pure clientside-rendering (CSR) dus geen probleem.

Bedrijfstoepassingen voor intern gebruik hoeven geen rekening te houden met crawlers en ook het prestatieprobleem is daar minder relevant of zelfs helemaal niet aanwezig. Minder is dus soms meer en maakt softwareprojecten eenvoudiger, beter te onderhouden, veerkrachtiger en daarmee in totaal voordeliger.

Voor sommige toepassingen zijn zowel MPA als SPA een stap te krachtig. Als het alleen gaat om het presenteren van statische inhoud, is Static Site Generation (SSG) een andere optie. Naast software voor SSG zoals Eleventy zijn ook de gangbare SSR-systemen in staat om statische pagina’s te genereren.

Er zijn dus veel toepassingen waarbij je geen van de momenteel populaire zwaargewichten hoeft in te schakelen. Pas als een ouderwetse servergebaseerde Rich Internet Application of een traditionele Single-Page-App niet aan de eisen voldoet en een pure SSG-oplossing niet voldoende is, wordt het tijd om serverside-rendering in overweging te nemen.

Voor veel toepassingen is de combinatie van SSR en webcomponenten een van de meest elegante architecturen die het web momenteel te bieden heeft.

MPA, SPA, SSR, SSG – OMG, WTF?

Vier afkortingen, één fundamenteel probleem: waar wordt de code uitgevoerd – op de server of in de browser? Afhankelijk van het antwoord ontstaan er webapplicaties met zeer uiteenlopende eigenschappen op het gebied van prestaties, SEO en onderhoudbaarheid.

De grenzen vervagen. Moderne frameworks zoals Next.js en SvelteKit combineren SSR, SSG en clientside-rendering, afhankelijk van de route. Hybride benaderingen zijn tegenwoordig de norm.

Peter Kröner en Noud van Kruysbergen

Inspiratie in je mailbox

Blijf bij op IT-gebied en verbreed je expertise. Ontvang elke week artikelen over de laatste tech-ontwikkelingen, toepassingen, nieuwe hard- en software én ontvang tips en aanbiedingen.

Loginmenu afsluiten