seojuice

SPA-SEO is een leveringsprobleem, geen renderingprobleem

Vadim Kravcenko
Vadim Kravcenko
· Updated · 13 min read

TL;DR: SPA-SEO is niet langer een rendering-probleem maar een delivery-probleem: URL’s, statuscodes, metadata en de hoofdinhoud moeten er al zijn voordat JavaScript iets mag doen, want Google rendert soms laat en veel AI-crawlers renderen helemaal niet.

Bij SPA-SEO gaat het niet meer om de vraag of Google “JavaScript ondersteunt”

Het meeste advies over SPA-SEO begint nog steeds met de verkeerde vraag: kan Google JavaScript renderen?

Ja. Google kan JavaScript renderen. Martin Splitt zegt dit al jaren, maar toch debuggen veel mensen nog steeds een SPA door eindeloos naar view-source: te kijken alsof dát de pagina is die Google indexeert.

“Veel mensen kijken nog steeds naar de broncode. Dat is niet wat wij gebruiken om te indexeren. Wij gebruiken de gerenderde HTML.”

Martin Splitt, Developer Advocate bij Google

Die uitspraak is belangrijk omdat ze één slechte gewoonte doorbreekt. Als je alleen de initiële broncode van een React-, Vue-, Angular-, SvelteKit-, Nuxt-, Remix- of Next.js-app bekijkt, mis je mogelijk wat Google uiteindelijk ziet. De gerenderde DOM telt.

Maar dat betekent niet dat client-side rendering veilig is voor elke publieke route. Rendering kost tijd. Rendering kan falen. Rendering kan later plaatsvinden dan het crawlen. Andere bots renderen misschien helemaal nooit. De echte vraag voor spa seo is of de crawler snel genoeg een betekenisvol document krijgt.

View source is een verkeerde debug-gewoonte

View source toont de eerste HTML-respons. Bij een klassiek CSR-appje is dat vaak een lege shell, één root-node, één scriptbundle en een schietgebedje. Google kan de pagina later renderen, de route uitvoeren, de API’s aanroepen en de echte content ontdekken. Kan.

Dat “kan” is waar rankings vreemd worden. De pagina kan er in je browser perfect uitzien maar toch fragiel zijn voor search — de browser is geduldig, crawlers werken met wachtrijen, budgetten, time-outs en faalmodi.

Gerenderde DOM is belangrijk, maar first-byte HTML wint nog steeds

seojuice.io is bewust gesplitst — publieke pagina’s leveren statische-HTML-eerst, het ingelogde dashboard gedraagt zich als een app. Twee renderingstrategieën onder één domein, omdat die routes verschillende taken hebben.

Blog, tools en landingspagina’s moeten gevonden, gecrawld, begrepen, gedeeld en geciteerd worden. Het dashboard hoeft niet te ranken op “page health scoring UI” en zal dat ook nooit doen. JavaScript mag de publieke pagina na het laden upgraden, maar de eerste respons moet al op een pagina lijken.

AI-crawlers hebben het risicoprofiel veranderd

Jarenlang zag ik dit als een puur Google-probleem (daarin zat ik fout). Nieuwe AI-crawlers hebben de oude shortcut alleen maar riskanter gemaakt.

“Uit de resultaten blijkt consequent dat geen van de grote AI-crawlers momenteel JavaScript rendert.”

Vercel Engineering

Als GPTBot, ClaudeBot, PerplexityBot of een andere crawler alleen een app-shell ziet, is je content voor dat kanaal feitelijk onzichtbaar. Google’s renderondersteuning helpt Google (en alleen Google) — het redt niet elke crawler, previewbot, monitoringtool, social parser of AI-ingestiesysteem.

Tijdlijn die vergelijkt hoe Googlebot, crawlers zonder JavaScript en AI-crawlers SPA-content zien
BRON: SEOJuice SPA-SEO-referentie, gebaseerd op Google’s rendering­documentatie en Vercels meting uit 2024 van JavaScript-uitvoering door AI-crawlers.

Bepaal welke SPA-routes pagina’s zijn en welke routes app-status

Deze stap slaan de meeste teams over. Ze vragen of de hele SPA SSR nodig heeft. Die vraagstelling verspilt ontwikkeltijd.

Een echte SPA mengt vrijwel altijd twee dingen: crawlebare pagina’s en privé app-statussen. Pricing-pagina’s, blogposts, documentatie, templates, integraties, vergelijkings­pagina’s en product­landingspagina’s zijn pagina’s. Dashboards, onboardingstappen, modals, filters, accountschermen en opgeslagen rapporten zijn app-status.

De oplossing begint met classificatie. Vraag engineers niet om “de SEO van de app te fixen”. Laat ze aangeven welke routes zoekverkeer verdienen en kies daarna per route de juiste rendering, indexering, canonicals en statuscodes.

Routetype Moet ranken? Rendermethode Indexregel
Blogpost Ja SSG of SSR Indexeren, self-canonical
Productlanding Ja SSG of SSR Indexeren, self-canonical
Zoekresultatenpagina Meestal niet CSR of SSR Vaak noindex
Dashboardroute Nee CSR is prima Blokkeren achter auth of noindex
Gefacetteerde filter-URL Soms SSR alleen als gecureerd Canonical of noindex
Beslisboom voor het kiezen van SEO-behandeling en render­model voor SPA-routes
BRON: SEOJuice-kader voor SPA-SEO-routeclassificatie.

Zo leg ik het uit in een technische SEO-audit. Een SPA met 40 publieke URL’s en 4.000 dashboard­statussen heeft niet 4.040 SEO-pagina’s. Ze heeft 40 pagina’s en een productinterface.

Dat onderscheid verandert de roadmap. Publieke routes hebben stabiele URL’s, self-canonicals, server-geleverde metadata, crawlebare links, bruikbare first-byte HTML en correcte statuscodes nodig. Dashboardroutes vereisen snelle interactie, auth, state-management en privacy. Beide groepen in één rendermodel persen maakt ze meestal allebei slechter.

Bij mindnow was dit de meest voorkomende SPA-fout die ik bij klantprojecten zag. Het team wilde één elegante frontend-architectuur. Search wilde saaie documenten. Het compromis was niet om de app te schrappen; het was om te stoppen met doen alsof elke route hetzelfde doel heeft.

Kies het juiste rendermodel vóór je SEO-tickets schrijft

Vergelijkingsgrafiek van SPA-rendermodellen voor SEO
BRON: SEOJuice-referentie voor renderstrategie, gebaseerd op Google’s rendering­documentatie en Vercels Next.js-gids.

CSR: prima voor dashboards, riskant voor landingspagina’s

Client-side rendering is vaak prima voor geauthenticeerde softwa­re­schermen. Moet een gebruiker inloggen, dan hoeft een crawler de route toch niet te indexeren. CSR wordt riskant wanneer dezelfde app-shell pricing-pagina’s, docs, artikelen en productpagina’s serveert waarvan de content pas verschijnt nadat JavaScript draait en API’s reageren.

SSG: saai, snel en meestal de juiste keuze

Bij static site generation worden pagina’s vooraf in HTML opgebouwd (meestal tijdens build of deploy). Voor blogs, docs, changelogs, glossariumpagina’s, templates en de meeste marketing­content is SSG nauwelijks te verslaan: snel, cachebaar, goedkoop en crawler-vriendelijk.

SSR: nuttig als publieke content vaak verandert

Server-side rendering past beter wanneer publieke content per request, locatie, voorraad, permissies of actualiteit verschilt. Lee Robinson vatte het basismodel van Next.js helder samen:

“Next.js prerendert de pagina bij elke request in HTML op de server.”

Lee Robinson, VP of Developer Experience bij Vercel

SSR geeft crawlers direct HTML zonder te wachten op de client-bundle, terwijl de pagina toch verse data kan tonen.

ISR: het praktische midden voor grote sites

Incremental Static Regeneration (ISR) — statische pagina’s die na publicatie worden ververst — is vaak het beste compromis voor grote contentbibliotheken. Je krijgt statische HTML voor de meeste requests en regeneratie wanneer content verandert. Voor programmatic SEO, docs en grote templatebibliotheken voorkomt ISR rebuild-pijn zonder terug te vallen op volledige CSR.

Dynamic rendering: de tijdelijke workaround

Bij dynamic rendering krijgt de crawler één versie en de gebruiker een andere. Handig om een legacy-SPA te redden wanneer migratie nog niet klaar is — maar ik zou er geen nieuwe zoekstrategie op bouwen.

“Ik zie dit echt als een tijdelijke workaround – waarbij ‘tijdelijk’ best een paar jaar kan betekenen – maar het blijft een oplossing met een houdbaarheidsdatum.”

John Mueller, Search Advocate bij Google

Dat is het juiste denkmodel. Gebruik dynamic rendering als tussenbrug en vervang die later door server-gerenderde of statisch-eerst publieke routes.

Los de SPA-crawlvallen op die indexering nog steeds breken

De harde SPA-SEO-fouten zijn meestal saai. Het zijn geen mysterieuze rankingpenalties maar leveringsbugs.

De eerste val is de universele shell. Elke URL geeft dezelfde 200 terug, dezelfde lege root-node en dezelfde bundle. De router beslist pas later of /pricing, /docs/api of /totally-fake-url bestaat. Dat vraagt te veel van crawlers en leidt tot de tweede val: soft 404’s.

“In plaats van met 404 te antwoorden, stuurt het gewoon 200 … en toont altijd een pagina op basis van de JavaScript-uitvoering.”

Martin Splitt, Developer Advocate bij Google

Ongeldige routes moeten echte 404- of 410-statuscodes teruggeven. Een leuke client-side “page not found”-component met een 200 blijft een slecht signaal (dit is de “soft-404”-val die je indexeerbudget sloopt).

Diagram dat het verschil toont tussen SPA-soft-404-responses en echte 404-statuscodes
BRON: SEOJuice-referentie voor SPA-SEO-crawlvallen, gebaseerd op Google’s soft-404-documentatie en de publieke uitleg van Martin Splitt.

De derde val is navigatie die crawlers niet kunnen volgen. Knoppen, click-handlers, custom componenten en router-events zijn prima voor interactie, maar interne ontdekking heeft nog steeds crawlebare anchors met echte href-waarden nodig. Als je belangrijkste pagina’s alleen bereikbaar zijn na een JavaScript-click-handler, is je crawlability zwakker dan je denkt.

Metadata is een andere veelvoorkomende misser. Veel SPA’s updaten titels, descriptions, canonicals, robots-tags, Open Graph-tags en schema pas na een route-wijziging. Dat werkt visueel in een browsertab, maar kan misgaan voor crawlers, social parsers en AI-bots. Route-specifieke metadata moet aanwezig zijn in de teruggegeven HTML voor elke indexeerbare URL.

Canonicals verdienen een eigen waarschuwing. Ik heb gehydrateerde apps een correcte canonical zien overschrijven met een staging-domein, een root-URL of de vorige route. Zo’n bug is stil. Niemand merkt het tot dubbele URL’s fout clusteren of de verkeerde pagina rankt.

Infinite scroll is nog een val als het content verbergt achter client-state. Als pagina twee, drie en verder geen crawlebare URL’s hebben, zullen zoekmachines ze nooit ontdekken. Gebruik gepagineerde fallback-URL’s voor belangrijke archieven en categoriepagina’s.

Ook API-geladen hoofdinformatie is fragiel. Als de H1, bodytekst, productdetails, reviews of interne links twee API-calls na hydratatie nodig hebben, heb je meer faalpunten. Botverkeer kan ratelimits raken, API’s kunnen onbekende user-agents blokkeren, time-outs kunnen de gerenderde DOM dun laten.

Hash-routing hoort niet thuis op indexeerbare publieke pagina’s. Een URL als /docs#pricing werkt voor fragmenten, maar hash-gebaseerde app-routing voor echte pagina’s maakt ontdekking, canonicalisatie en analytics onnodig lastig.

Let tenslotte op auth en bundlegewicht samen. Publieke content die per ongeluk achter login-checks zit kan verdwijnen uit crawls. Zware bundles vertragen rendering en verspillen crawlbudget. Beide problemen lijken “JavaScript-SEO”, maar de praktische oplossing is schonere route-grenzen en minder client-werk voor publieke pagina’s.

Bouw elke indexeerbare route eerst als document, daarna pas als app

De beste SPA-SEO-regel die ik ken is simpel: als de route zoekverkeer verdient, moet de eerste respons op een pagina lijken.

Dus elke publieke URL moet bruikbare HTML teruggeven met de kernsignalen al aanwezig:

  • Correct <title>.
  • Meta-beschrijving.
  • Self-refererende canonical.
  • Eén duidelijke H1.
  • Hoofdinhoud.
  • Crawlebare interne links.
  • Gestructureerde data waar relevant.
  • Correcte statuscode.
  • Open Graph- en Twitter-tags als delen belangrijk is.
HTML-eerst SPA-pagina­structuur met JavaScript-hydratatie na de kern-SEO-elementen
BRON: SEOJuice SPA-SEO-architectuur­playbook voor HTML-eerst publieke routes.

Daarna kan JavaScript componenten hydrateren, elementen personaliseren, calculators laden, events tracken, tabellen filteren en de ervaring verrijken. Maar het mag niet nodig zijn voor de crawler om te begrijpen waar de pagina over gaat.

Hier kruisen site-architectuur en SPA-SEO elkaar. Een publieke route zonder crawlebare links ernaartoe blijft zwak, zelfs als hij server-gerenderd is. Een prachtig gerenderd document dat vijf klikken diep ligt achter client-only navigatie presteert niet zoals een pagina in een helder intern linksysteem.

De document-eerst-regel houdt teams eerlijk. Pricing is een document. Een blogpost is een document. Een docs-pagina is een document. Een opgeslagen dashboardfilter, open modal of onboardingstap is app-state. App-state behandelen als zoekpagina zorgt voor index-opblazing. Publieke pagina’s behandelen als app-state maakt ze onzichtbaar.

Bij seojuice.io is die splitsing bewust. Publieke routes moeten suf genoeg zijn voor crawlers. Het product mag na inloggen interactief blijven. Die twee ideeën kunnen prima samenleven.

Test SPA-SEO met gerenderde HTML, niet op goed geluk

Als je alleen de browserervaring test, test je het meest roze pad. SPA-SEO heeft lelijkere tests nodig.

  1. Haal de URL op met JavaScript uitgeschakeld en controleer of de content nog logisch is.
  2. Inspecteer de URL in Google Search Console en bekijk de gerenderde HTML.
  3. Vergelijk de initiële HTML met de gerenderde DOM in Chrome DevTools.
  4. Test statuscodes direct met curl -I https://example.com/missing-route.
  5. Crawl de site met één JS-capabele crawler en één crawler zonder JS.
  6. Bevestig dat titles, canonicals, robots-tags, schema en interne links bestaan vóór hydratatie.
  7. Check serverlogs op bot-hits, geblokkeerde API’s, time-outs en onverwachte redirects.
  8. Valideer gestructureerde data met Google’s Rich Results Test na rendering.

De ongemakkelijke test is de H1-test. Als Googlebot vijf stappen en twee API-calls nodig heeft om de H1 te vinden, is de pagina fragiel, zelfs als hij uiteindelijk wordt geïndexeerd.

Screaming Frog, Sitebulb, Google Search Console, Chrome DevTools, Rich Results Test en serverlogs helpen allemaal. Welke tool je gebruikt is minder belangrijk dan de vergelijking. Je wilt weten wat er bij de eerste respons bestaat, wat er na rendering verschijnt en wat Google daadwerkelijk heeft geïndexeerd.

Hier stoppen veel JavaScript-SEO-audits te vroeg. Ze bewijzen dat Google één pagina kan renderen. Prima. Test nu ongeldige routes, paginatie, canonical-wijzigingen, metadata­wijzigingen, API-fouten, trage responsen en niet-Google-crawlers.

SPA-SEO best-practice-checklist

Gebruik deze checklist per route. Een site-wide “pass” verbergt te veel SPA-fouten.

  • Rendering: Publieke pagina’s gebruiken SSG, SSR of ISR. Privé app-schermen mogen CSR gebruiken.
  • Routing: Elke indexeerbare URL heeft een unieke route, unieke content en een self-canonical.
  • Statuscodes: Missende pagina’s geven 404 of 410 terug, geen 200.
  • Links: Interne navigatie gebruikt crawlebare anchors met echte href-attributen.
  • Metadata: Titles, descriptions, canonicals, robots-tags, Open Graph-tags en schema zijn route-specifiek.
  • Content: Hoofdtekst, H1’s, productinformatie en kernlinks bestaan zonder te wachten op client-only data.
  • Performance: Bundle-grootte, hydratatie-kosten, third-party scripts en route-level code-splitting zijn onder controle.
  • Indexcontrole: Dashboards, privé-routes, low-value filters en dunne zoekpagina’s zijn geblokkeerd of noindexed.
  • Testing: Initiële HTML, gerenderde DOM en geïndexeerde content worden vergeleken op belangrijke templates.
  • AI-zichtbaarheid: Belangrijke content staat in HTML omdat veel AI-crawlers geen JavaScript renderen.

“Als het niet gecrawld wordt, kan het niet in search verschijnen. Ongeacht het kanaal.”

Jamie Indigo, Technical SEO Consultant bij Not a Robot

Die zin is de hele checklist in één regel. Search, AI-antwoorden, link-previews en discovery-systemen hangen eerst van toegankelijkheid af. Rankings komen pas daarna.

De eenvoudigste SPA-SEO-architectuur die ik vandaag zou uitrollen

Als ik nu een moderne SPA zou bouwen met zoekverkeer in gedachten, zou ik niet het hele product server-renderen. Ik zou het opsplitsen.

Site-onderdeel Aanbevolen aanpak
Marketing site Statische generatie
Blog en docs Statische generatie of ISR
Productpagina’s SSR of ISR
Programmatic SEO-pagina’s Statische generatie met strenge pruning
Dashboard CSR achter auth
Zoek- en filterpagina’s Noindex tenzij handmatig gecureerd
Ongeldige routes Echte 404 of 410
Gedeelde layout Server-gerenderde metadata en navigatie

Zo zou ik het bij seojuice.io opdelen. Marketingpagina’s en artikelen moeten HTML-eerst zijn. Productonderdelen die versheid nodig hebben kunnen SSR of ISR gebruiken. Het dashboard kan app-achtig blijven, want ranken heeft daar geen zin.

Programmatic-SEO-pagina’s vragen extra terughoudendheid. Statische generatie maakt het makkelijk om duizenden pagina’s aan te maken, ook die niemand moet indexeren. Genereer alleen pagina’s met echte zoekvraag, bruikbare content en interne links. Snoei de rest weg voordat Google die beslissing voor je neemt.

De winnende SPA is niet degene die bewijst dat crawlers JavaScript kunnen uitvoeren. De winnende SPA is degene die crawlers geen onnodig werk laat doen.

FAQ

Kan een single-page application ranken in Google?

Ja. Een SPA kan ranken als indexeerbare routes crawlebare content, correcte metadata, interne links en geldige statuscodes teruggeven. Google kan JavaScript renderen, maar voor alles op rendering vertrouwen maakt de site fragieler.

Is server-side rendering verplicht voor SPA-SEO?

Nee, niet voor elke route. SSR is nuttig voor publieke pagina’s met veranderende content. SSG of ISR is vaak beter voor stabiele content. CSR is prima voor privé-dashboards, accountschermen en app-states die niet geïndexeerd hoeven worden.

Zijn hash-routes slecht voor SEO?

Hash-routes zijn geen goede keuze voor indexeerbare pagina’s. Ze werken prima voor on-page fragmenten, maar publieke content hoort schone URL’s, route-specifieke metadata en server-niveau statuscodes te hebben.

Moeten zoekresultaten­pagina’s van een SPA worden geïndexeerd?

Meestal niet. Interne zoekpagina’s en gefacetteerde filters leveren vaak dunne of duplicatieve URL’s op. Gecureerde filterpagina’s kunnen worden geïndexeerd als ze unieke vraag, stabiele content en een duidelijke canonical-strategie hebben.

Hoe weet ik of mijn SPA een soft-404-probleem heeft?

Vraag een nep-URL op en controleer de statuscode. Als /this-page-should-not-exist een 200 teruggeeft met een client-side not-found-bericht, loop je soft-404-risico.

Hulp nodig om je SPA om te zetten in crawlebare pagina’s?

SEOJuice helpt teams om crawlebare interne links te versterken op de publieke pagina’s die werkelijk zoekverkeer verdienen. Heeft je SPA verweesde routes, weggestopte templates of pagina’s die Google nooit lijkt te bereiken, dan kan interne-linking-automatisering de documentlaag toegankelijker maken voor crawlers.

SEOJuice
Stay visible everywhere
Get discovered across Google and AI platforms with research-based optimizations.
Works with any CMS
Automated Internal Links
On-Page SEO Optimizations
Get Started Free

no credit card required

More articles

No related articles found.