
Jarenlang was Vercel de standaardplek waar we bij WebCatalog bijna alles voor het web uitrolden. De meeste van die projecten gebruikten Next.js, dus die combinatie lag voor de hand. Onze marketingsites, productinterfaces, accountinstellingen, ontwikkelaarsconsole, authenticatie, beheertools en API's draaiden uiteindelijk allemaal op ongeveer dezelfde stack.
Dat werkte lange tijd goed. Vercel maakte deployments eenvoudig, Next.js gaf ons een productief full-stackframework en we hoefden zelden na te denken over de onderliggende infrastructuur.
Uiteindelijk sloot dat gemak niet meer aan bij wat onze producten nodig hadden.
Onze marketingsites hadden nog steeds baat bij SSR (server-side rendering), maar de meeste van onze andere webapps niet. Die konden gewoon SPA's (single-page applications) zijn. Onze API's hadden behoefte aan een normale Node.js-omgeving met native afhankelijkheden en langlopende processen. Bijna alles in de rest van onze frontendstack gebruikte al Vite. Tegelijkertijd werden de infrastructuurkosten steeds moeilijker te negeren, vooral doordat publiek en geautomatiseerd verkeer toenam.
Aanvankelijk probeerden we te optimaliseren wat we al hadden. We overwogen Next.js te behouden en het via een adapter naar Cloudflare Workers te verplaatsen. We probeerden Astro voor de marketingsites. Ook draaiden we onze API's korte tijd op Cloudflare Containers.
Uiteindelijk kwamen we uit bij iets eenvoudigers: de meeste van onze webapps gebruiken nu Vite + TanStack Router op Cloudflare Workers, onze marketingsites gebruiken TanStack Start met SSR op Cloudflare Workers, en onze API's gebruiken Hono + tRPC op Railway als langlopende Node.js-containers.
Door de migratie daalden onze kosten voor webinfrastructuur met ongeveer 80%. Nadat we ook onze beveiligingsregels bij Cloudflare hadden aangescherpt en meer vijandig geautomatiseerd verkeer aan de edge blokkeerden, liep de totale besparing op tot ongeveer 90%.
Het grootste deel van de migratie werd bovendien uitgevoerd door AI-codeeragents, voornamelijk Claude Fable 5 en Claude Opus 5. Engineers bepaalden de architectuur, stelden randvoorwaarden vast, beoordeelden wijzigingen en controleerden het resultaat.
We begonnen niet met een vertrek bij Vercel
Onze eerste ingeving was om de bestaande opzet te optimaliseren.
webcatalog.io was het meest voor de hand liggende startpunt: de site krijgt veel publiek verkeer, terwijl de meeste pagina's relatief weinig veranderen. We ontdekten dat nog te veel van de site dynamisch werd gerenderd. Daarom verholpen we onbedoelde dynamische rendering, voegden we ISR (Incremental Static Regeneration) toe aan routes met veel verkeer, maakten we meer pagina's cachebaar en optimaliseerden we kostbaar sitemapgedrag.
Dat werk hielp, maar maakte het onderliggende probleem ook duidelijker. We besteedden steeds meer tijd aan uitzoeken waarom relatief statische inhoud dynamisch was, welk frameworkgedrag dat veroorzaakte en welk frameworkmechanisme we moesten inzetten om de inhoud weer cachebaar te maken.
Veel van onze andere applicaties hadden ondertussen juist het tegenovergestelde probleem. Accountinstellingen, de ontwikkelaarsconsole, beheerapplicaties en de meeste productinterfaces hadden helemaal geen server-side rendering nodig. Het waren browserapplicaties die met API's communiceerden.
Vanaf dat moment was de vraag niet meer: ‘Hoe verlagen we onze Vercel-rekening?’, maar: ‘Wat zouden we bouwen als deze applicaties nog niet in Next.js waren gebouwd?’
We overwogen Next.js op Cloudflare
De minst ingrijpende optie was om Next.js te behouden en het van Vercel naar Cloudflare Workers te verplaatsen.
We overwogen dit serieus, omdat Next.js via adapters op Cloudflare kan draaien. Daarmee hadden we een groot deel van de bestaande applicatiestructuur kunnen behouden.
Maar we zagen Next.js op Cloudflare niet als gelijkwaardig aan Next.js op Vercel. Next.js is het nauwst geïntegreerd met de runtime, het deploysysteem, het cachegedrag en de platformfuncties van Vercel. Op Cloudflare moet een adapter die aannames vertalen naar een andere runtime.
Dat kan goed werken, maar als ons doel was de stack te vereenvoudigen, voelde een extra compatibiliteitslaag niet ideaal.
Daarnaast speelde een bredere kwestie rond tooling. Bijna alles wat we verder voor de frontend bouwden, gebruikte al Vite: desktopapplicaties, browserextensies, SPA's en andere client-side projecten.
Next.js was sterk in de richting van Turbopack opgeschoven, en Turbopack is enorm verbeterd. Dit was geen klacht over prestaties. Voor ons zat het grotere verschil in het ecosysteem. Vite vormde al de gemeenschappelijke basis voor het grootste deel van onze frontendcode, met een breder ecosysteem van plug-ins en configuraties die we tussen projecten beter konden hergebruiken.
Next.js op Cloudflare houden zou dus een deel van het hostingprobleem hebben opgelost, maar zowel het verschil tussen frameworks als een afzonderlijk ecosysteem voor buildtools in stand hebben gehouden.
Daarom deden we een stap terug en bekeken we wat elke workload daadwerkelijk nodig had.
We deelden de stack op naar workload
Toen we eenmaal stopten met zoeken naar één universele vervanger voor Next.js, werd de architectuur veel eenvoudiger.
Onze marketingsites hadden SSR nodig vanwege hun publieke, gelokaliseerde pagina's, metadata, canonieke URL's, sitemaps en verkeer uit zoekmachines.
De meeste van onze andere webapps hadden geen SSR nodig en konden eenvoudig Vite-applicaties met TanStack Router zijn.
Onze API's hadden helemaal geen React-framework nodig en waren beter af in een normale Node.js-runtime.
De uiteindelijke verdeling zag er zo uit:
Webapps
Vite + TanStack Router
│
└── Cloudflare Workers
Marketingsites
TanStack Start + SSR
│
└── Cloudflare Workers
API's
Hono + tRPC
│
└── Railway / Node.js-containers
De regel werd eenvoudig: gebruik SSR waar het echt waarde toevoegt, gebruik een gewone SPA waar dat niet zo is, en draai backendservices in een backendruntime.
De meeste webapps werden SPA's
De migraties naar SPA's waren het eenvoudigste deel.
Vooral de WebCatalog-webapp verhuisde snel: we zetten de vervangende app met Vite + TanStack Router op, migreerden de interface, schakelden de deployment over naar Cloudflare Workers en verwijderden diezelfde ochtend nog de oude Next.js-versie.
We herhaalden hetzelfde patroon voor de Lexibird-webapp, accountinstellingen, onze ontwikkelaarsconsole, authenticatie en beheerapplicaties. Van het opzetten van de eerste SPA tot het verwijderen van de laatste Next.js-applicatie duurde het ongeveer 19 dagen.
Voor deze applicaties is Cloudflare Workers bewust ongecompliceerd. Vite bouwt statische bestanden, Cloudflare serveert ze en applicatieroutes vallen terug op index.html. Er is geen SSR-runtime, omdat er niets op de server gerenderd hoeft te worden.
Het lastigere deel was het behouden van het gedrag dat zich in de loop der tijd rond die applicaties had opgebouwd. Sommige authenticatielogica moest van Next.js-serverroutes naar de API worden verplaatst, en oudere versies van de WebCatalog-desktopapp waren nog afhankelijk van verouderde endpoints die moesten blijven werken.
De React-interface verplaatsen was eenvoudig.
Bestaande afspraken behouden was moeilijker.
We probeerden Astro en verwijderden het vijf dagen later
De marketingsites waren lastiger.
Onze eerste keuze was Astro. Dat leek een logische keuze voor webcatalog.io en lexibird.com, omdat het allebei publieke sites met veel inhoud zijn en Astro goed integreert met Cloudflare.
We begonnen beide sites te migreren en stopten vijf dagen later.
Er was niet één fataal probleem. In plaats daarvan stapelden kleine verschillen zich op. Sommige routes hadden tijdens het prerenderen toegang tot de database nodig, terwijl onze buildomgeving bewust geen toegangsgegevens voor de productiedatabase had. Met React islands voelde gedeelde applicatiestatus minder vanzelfsprekend. Ook liepen we tegen verschillen aan tussen vooraf gerenderde routes en routes die tijdens runtime werden gerenderd, en tegen enige frictie met de tooling in onze repository.
Geen van die problemen was onoplosbaar. Juist daarom was doorgaan riskant. We hadden gemakkelijk nog een paar weken kunnen besteden aan het ene na het andere probleem.
In plaats daarvan verwijderden we de implementatie en begonnen we opnieuw.
AI-agents veranderden de afweging achter die beslissing. Een groot deel van het routinematige migratiewerk had geen weken aan engineerstijd gekost, waardoor de verzonken kosten lager waren en we makkelijker konden toegeven dat een andere architectuur onze voorkeur had.
TanStack Start paste beter
We begonnen de migratie van de marketingsites opnieuw vanuit de oorspronkelijke Next.js-code, ditmaal met TanStack Start.
Dat sloot veel natuurlijker aan, omdat we TanStack Router al gebruikten in de SPA-applicaties. Routing, loaders, zoekparameters en navigatie volgden daardoor vergelijkbare concepten. De code bleef gewone React en TypeScript, en het buildsysteem was Vite.
We migreerden lexibird.com in één dag naar TanStack Start. webcatalog.io volgde daarna.
Het voordeel was niet dat TanStack Start elk probleem op magische wijze oploste. Het voordeel was dat het aansloot bij de richting die de rest van onze stack al opging.
In een monorepo is dat belangrijk. Gedeelde buildtools betekenen meer herbruikbare plug-ins en configuratie, minder uitzonderingen bij het debuggen, minder omschakelen tussen verschillende werkwijzen voor ontwikkelaars en minder projectspecifieke conventies die codeeragents moeten ontdekken.
Cloudflare was veel goedkoper voor ons verkeer
De architectuur was maar een deel van de reden waarom onze rekening daalde. De prijsstelling van Cloudflare was een belangrijke factor, vooral voor bandbreedte.
Voor Cloudflare Workers Paid wordt momenteel vooral betaald op basis van verzoeken en CPU-gebruik; er komen geen kosten voor gegevensoverdracht of bandbreedte bij voor Workers. (developers.cloudflare.com) Dat maakt veel uit voor publieke websites: een verzoek kan heel weinig CPU gebruiken en toch HTML, JavaScript, afbeeldingen, lettertypen en andere bestanden overdragen.
Het prijsmodel van Vercel omvat ook netwerkgerelateerde gebruikscategorieën en inbegrepen hoeveelheden, waaronder Fast Data Transfer en Fast Origin Transfer. (vercel.com) Voor ons verkeersprofiel was Cloudflare aanzienlijk voordeliger.
De besparing kwam niet alleen door van provider te veranderen. We zetten ook veel applicaties om naar statische Vite-builds, verminderden onnodige SSR, maakten caching explicieter en verplaatsten API-workloads naar een runtime die beter bij ze paste. Maar de prijsstelling van Cloudflare, met name voor bandbreedte, droeg in belangrijke mate bij aan de daling van ongeveer 80% die we zagen.
Dat percentage geldt voor onze specifieke workload. We beweren niet dat elke applicatie die van Vercel naar Cloudflare verhuist 80% zal besparen.
De API's kwamen op Railway terecht
De API's vormden een apart vraagstuk.
Hoewel het Next.js-projecten waren, waren ze al grotendeels onafhankelijk van Next.js. De WebCatalog-API telde ongeveer 34.000 regels, maar slechts zeven bestanden importeerden next/server. tRPC gebruikte al zijn Fetch-adapter en Inngest ondersteunde Hono.
We vervingen de Next.js-laag voor HTTP-verkeer door Hono. Dat bleek verrassend weinig werk.
De lastigere vraag was waar we de API's zouden draaien. Gewone Workers waren geen goede keuze, omdat de API's Node.js en native afhankelijkheden zoals sharp gebruiken.
Aanvankelijk probeerden we Cloudflare Containers, waarmee we snel van Vercel af konden. Maar nadat we die opzet in productie hadden gebruikt, merkten we dat het operationele model meer handmatige afstemming van de capaciteit vereiste dan we op dat moment wilden.
Dus verhuisden we de API's opnieuw.
Tegenwoordig draaien ze op Railway als gewone, langlopende Node.js-containerdiensten. CI (continuous integration) bouwt Docker-images en pusht ze naar ons register; Railway verzorgt het horizontaal opschalen.
Niet alles hoeft aan de edge te draaien.
Weggaan bij Vercel betekende meer zelf beheren
De lagere kosten gingen gepaard met een afweging.
Vercel en Next.js hadden veel infrastructuurgedrag op een hoger niveau afgehandeld. Met TanStack Start en Workers gingen we in plaats daarvan meer uit van expliciete HTTP-caching. Een pagina wordt gerenderd, we sturen cacheheaders terug en Cloudflare slaat de respons op in de cache. Voor browser- en edgecaching kunnen verschillende regels gelden, waaronder stale-while-revalidate-gedrag aan de edge.
We vonden het prettig dat die keuzes expliciet werden, maar expliciete infrastructuur betekent ook dat fouten je eigen verantwoordelijkheid zijn.
We maakten er een paar. Eén cacheregel in productie was per ongeluk ook van toepassing op bezoekersspecifieke antwoorden van TanStack-serverfuncties. Een andere cacheconfiguratie werkte slecht samen met het terugvalgedrag van de SPA. Die fouten herinnerden ons eraan dat je de correctheid van een migratie niet volledig vanuit de repository kunt aantonen. De productie-infrastructuur maakt ook deel uit van het systeem.
AI-agents veranderden onze manier van migreren
Het grootste deel van het implementatiewerk werd uitgevoerd door codeeragents, voornamelijk Claude Fable 5 en Claude Opus 5.
Frameworkmigraties lenen zich bijzonder goed voor agents, omdat er voor veel werk een duidelijke bestaande referentie is. Verplaats deze route, behoud deze URL, vervang deze framework-API, behoud dezelfde metadata, voer de typecheck uit, verhelp de fouten en vergelijk het resultaat met de oude implementatie.
Voor webcatalog.io hielden we een migratieoverzicht bij waarin het werk was verdeeld in fasen zoals basis, productpagina's, cataloguspagina's, zoeken, blog, prijzen, redirects, sitemaps en omschakeling. In plaats van een agent te vragen ‘migreer webcatalog.io naar TanStack Start’, gaven we die afgebakende taken met expliciete eisen die niet mochten veranderen.
De bestaande applicatie werd de specificatie. Het was de taak van de agent om met de nieuwe architectuur hetzelfde gedrag te realiseren, niet om het product opnieuw uit te vinden.
Daardoor verschoof de menselijke inzet van het handmatig omzetten van code naar vragen als: Heeft deze pagina echt SSR nodig? Welk gedrag is bewust zo ontworpen? Wat mag voor iedereen worden gecachet? Welke URL's moeten exact hetzelfde blijven? Waar moet authenticatie plaatsvinden? Wanneer moeten we stoppen met proberen een aanpak werkend te krijgen?
We bleven het resultaat beoordelen, builds en typechecks uitvoeren en onderdelen met een hoger risico, zoals authenticatie, SEO (zoekmachineoptimalisatie), redirects en caching, handmatig controleren.
De belangrijke verandering was niet dat AI code voor ons schreef. Het was dat AI experimenteren goedkoper maakte.
Daardoor werd het veel makkelijker te rechtvaardigen dat we Astro probeerden en het na vijf dagen weer verwijderden. De API's een keer verplaatsen en vervolgens besluiten dat een ander platform beter paste, was minder pijnlijk. Het routinematige werk kostte minder, waardoor we van richting konden veranderen als de architectuur niet klopte, in plaats van door te gaan omdat we er al te veel in hadden geïnvesteerd.
Agents maakten implementatiewerk minder schaars.
Daardoor werden architectuur, randvoorwaarden, beoordeling en verificatie belangrijker.
80% werd ongeveer 90%
Na de migratie waren onze kosten voor webinfrastructuur ongeveer 80% lager.
Daarna gingen we beter letten op welke verzoeken de applicaties überhaupt bereikten.
Een aanzienlijk deel van het verkeer op het openbare internet is geautomatiseerd: zoekmachines, AI-crawlers, agents, scrapers, scanners en minder vriendelijke bots. Een deel van dat verkeer is nuttig, een ander deel niet.
Nu de applicaties direct achter Cloudflare stonden, scherpten we onze beveiligingsregels aan. Zo kon vijandig geautomatiseerd verkeer aan de edge worden geweigerd voordat het rekenwerk in de applicatie of databasewerk veroorzaakte.
Daarna liep onze totale besparing op tot ongeveer 90% ten opzichte van de oude opzet.
Die uiteindelijke daling kwam door een combinatie van factoren: de lagere prijzen van Cloudflare, vooral voor bandbreedte; minder server-side werk; een betere runtime voor onze API's; explicietere caching; en minder onnodige verzoeken die de applicaties überhaupt bereiken.
Waar we zijn uitgekomen
Er staan geen Next.js-applicaties meer in onze repository. De meeste van onze webapps zijn nu SPA's met Vite + TanStack Router op Cloudflare Workers. webcatalog.io en lexibird.com gebruiken TanStack Start op Cloudflare Workers, omdat die sites daadwerkelijk baat hebben bij SSR. Onze API's gebruiken Hono + tRPC op Railway en draaien als gewone Node.js-containers. Bijna al onze frontendprojecten maken nu deel uit van het Vite-ecosysteem.
Onze infrastructuurkosten daalden met ongeveer 80%, mede dankzij het feit dat Cloudflare voor ons verkeer aanzienlijk voordeliger is, vooral wat bandbreedte betreft. Door vijandig geautomatiseerd verkeer aan de edge te filteren, liep de totale besparing op tot ongeveer 90%.
Maar het resultaat dat we het belangrijkst vinden, is architecturaal. Marketingsites krijgen SSR, alles wat een SPA kan zijn is een SPA, en API's draaien op echte servers wanneer echte servers eenvoudiger zijn.
We begonnen met de vraag hoe we Vercel goedkoper konden maken.
Uiteindelijk beseften we dat we het grootste deel van de architectuur waarvoor we betaalden helemaal niet nodig hadden.