SEO för single page-applikationer: varför SPA:er inte rankas, hur SSR, statisk generering och förrendering löser det, och hur du gör din app läsbar för AI-crawlers.

På den här sidan
Kort sagt
en single page-applikation (SPA) laddar ett enda HTML-skal och bygger varje vy med JavaScript i webbläsaren. Google kan köra JavaScript, men renderingen sker med fördröjning och är skör, och crawlers bakom ChatGPT, Claude och Perplexity kör den inte alls. För att rankas och bli citerad behöver varje vy i en SPA en egen URL, en riktig länk med en href, en unik titel och beskrivning, en korrekt statuskod och framför allt att huvudinnehållet finns i den HTML som servern skickar, genom serverrendering, statisk generering eller förrendering.
Vill du veta mer? Fråga
Snabbquiz
Vem ska åtgärda den tekniska sidan av din webbplats?
Våra råd till dig
Ge varje vy en ren URL som går att genomsöka är rätt lösning för dig
Nummer 1 i den här rankningen, den bästa matchningen för dina svar.
Våra råd till dig
Använd riktiga länkar är rätt lösning för dig
Nummer 2 i den här rankningen, den bästa matchningen för dina svar.
Våra råd till dig
Uppdatera titel, beskrivning och kanonisk länk per rutt är rätt lösning för dig
Nummer 3 i den här rankningen, den bästa matchningen för dina svar.
Våra råd till dig
Returnera ärliga statuskoder är rätt lösning för dig
Nummer 4 i den här rankningen, den bästa matchningen för dina svar.
SEO för single page-applikationer är de tekniker som gör en JavaScript-app byggd med React, Vue, Angular eller Svelte lika lätt att genomsöka som en klassisk webbplats. Problemet är väl dokumenterat: Google förklarar i sina grunder för JavaScript-SEO att de behandlar JavaScript-sidor i tre faser (genomsökning, rendering, indexering), och varje svaghet hos en SPA sitter någonstans i den kedjan. Den här guiden fokuserar på SPA:er; för JavaScript-webbplatser i allmänhet, läs vår guide om JavaScript-SEO.
En klassisk webbplats returnerar ett komplett HTML-dokument för varje URL. En SPA returnerar nästan samma dokument för varje URL: en tom <div id="root">, några metataggar och ett stort JavaScript-paket. Webbläsaren laddar ner paketet, anropar ett API och ritar sedan sidan. När besökaren klickar på en länk byter JavaScript ut innehållet utan att sidan laddas om.
För en människa med en snabb telefon känns resultatet omedelbart. För en crawler är det första svaret ett tomt skal: ingen rubrik, ingen text, inga länkar. Allt beror på om roboten kör JavaScriptet, och när. Den enda faktan förklarar de flesta problemen med SEO för SPA:er:
/#/pricing), och delen efter # skickas aldrig till servern, så alla vyer slås ihop till en URL.<title>, och om routern inte uppdaterar den delar varje sida den.href: knappar och onClick-hanterare navigerar för användarna men ger crawlers ingenting att följa.Googlebot hämtar först den råa HTML:en och läser länkarna och direktiven som finns där. Sidor som returnerar statuskoden 200 placeras sedan i en kö för Web Rendering Service, en headless Chromium. Google säger att en sida „kan stanna i kön i några sekunder, men det kan ta längre tid än så”. När sidan är renderad analyseras den resulterande HTML:en igen för innehåll och länkar, och indexeras sedan.
Fyra detaljer i Googles dokumentation spelar roll för SPA:er:
<a>-element med en href är pålitliga länkar. Länkar som JavaScript lägger till är bra så länge de följer det formatet.pushState) i stället för #-rutter. Google avvecklade sitt gamla AJAX-genomsökningsschema (URL:erna med #!) i oktober 2015.noindex i den ursprungliga HTML:en gäller. Google kan hoppa över renderingen när den ser noindex, så att ta bort den med JavaScript senare fungerar inte.Den praktiska konsekvensen: Google kan indexera en klientrenderad SPA, men du lägger till ett andra steg, en fördröjning och en felpunkt på varje sida. En timeout, ett blockerat API-anrop eller ett JavaScript-fel under renderingen lämnar den indexerade versionen tom. Kontrollera vad Google ser med verktyget URL-kontroll i Search Console, och läs vår guide om hur Googles indexering fungerar om sidor fortsätter att saknas i indexet.
Oftast inte. I en studie publicerad i december 2024 om trafiken i sitt nätverk fann Vercel och MERJ att „inga av de större AI-crawlerna för närvarande renderar JavaScript”: GPTBot, OAI-SearchBot och ChatGPT-User (OpenAI), ClaudeBot (Anthropic), PerplexityBot, Meta-ExternalAgent och Bytespider läser alla bara den råa HTML:en. De laddar ner JavaScript-filer (ungefär 11,5% av ChatGPTs förfrågningar och 23,8% av Claudes) men kör dem inte. Undantagen är Gemini, som bygger på Googles renderingsinfrastruktur, och AppleBot, som använder en webbläsarbaserad crawler.
För en klientrenderad SPA betyder det att AI-crawlerna som matar ChatGPT, Claude och Perplexity ser en tom sida. Din produkt kan rankas på Google och ändå vara osynlig i AI-svar, eftersom assistenten aldrig läste ett ord av den. Samma gäller för de flesta länkförhandsvisningsrobotar i sociala nätverk och meddelandeappar, som läser Open Graph-taggarna i den råa HTML:en. När innehållet väl finns i HTML:en, kontrollera också att din robots.txt tillåter de AI-crawlers du vill ha.
Lösningen är att skicka riktig HTML i det första svaret. Google kallar dynamisk rendering (att bara skicka en förrenderad version till robotar) „en lösning för stunden och inte en rekommenderad lösning” i sin dokumentation om dynamisk rendering, och pekar i stället på serverrendering, statisk rendering eller hydrering. Definitionerna nedan följer referensen Rendering on the Web på web.dev.
| Strategi | Hur den fungerar | Innehåll i första HTML:en | Bäst för | Begränsningar |
|---|---|---|---|---|
| Rendering i klienten (CSR) | Webbläsaren bygger sidan från JavaScript och ett API | Nej | Instrumentpaneler och sidor bakom en inloggning | Fördröjd indexering på Google, osynlig för de flesta AI-crawlers |
| Serverrendering (SSR) | Servern renderar HTML vid varje förfrågan, sedan hydrerar JavaScript den | Ja | Kataloger, listor, innehåll som ändras ofta eller är personligt | Serverkostnad, långsammare svarstid om den inte cachas |
| Statisk generering (SSG) | HTML genereras vid bygget för varje URL och levereras från ett CDN | Ja | Marknadsföringssidor, blogg, dokumentation, landningssidor | Ombyggnad krävs när innehållet ändras (inkrementella alternativ finns) |
| Förrendering vid bygget | En headless webbläsare fångar startläget för varje rutt som statisk HTML | Ja | Befintliga SPA:er med ett begränsat antal publika rutter | Data fryst vid bygget, rutterna måste listas |
| Dynamisk rendering | Robotar får en förrenderad ögonblicksbild, användare SPA:n | Ja, bara för listade robotar | Tillfällig lösning för en äldre app | Officiellt en lösning för stunden, underhållskostnad, risk att robotar får annat innehåll |
De flesta moderna ramverk låter dig blanda strategier per rutt: statiskt för marknadsföringssidor, serverrenderat för katalogen, klientrenderat för den inloggade appen. Den hybrida uppsättningen ger sökrobotar fullständig HTML där söktrafiken finns och behåller SPA-upplevelsen där den tillför värde.
| Bibliotek | Ramverk eller funktion för HTML vid första laddning |
|---|---|
| React | Next.js (serverkomponenter, SSR och statisk generering), React Router i ramverksläge, Gatsby |
| Vue | Nuxt (universell rendering som standard, statisk generering tillgänglig) |
| Angular | Angular SSR (serverrendering och förrendering vid bygget) |
| Svelte | SvelteKit (serverrendering som standard, förrendering per rutt) |
| Vilket som helst av dem | Astro (statisk HTML som standard, interaktiva öar laddas bara där det behövs) |
Webbplatser som genereras av AI-webbplatsbyggare förtjänar en särskild notering: många exporterar en klientrenderad React-app. Om du byggt din webbplats med en sådan, läs våra anteckningar om SEO och GEO för Lovable innan du antar att Google och ChatGPT kan läsa den.
Använd History API (/pricing, aldrig /#/pricing), en URL per innehållsstycke, och samma URL oavsett om besökaren kommer direkt eller via navigering i appen. Varje URL måste ladda rätt innehåll när den öppnas i en ny flik, vilket är testet en crawler gör.
Varje navigeringselement som leder till en indexerbar sida bör vara en <a href="/path">. Ramverkens länkkomponenter (Link, NuxtLink, routerLink) skapar den här märkningen; egna klickhanterare på div- eller button-element gör det inte. Bra länkar bär också din webbplatsarkitektur: om en sida inte är länkad hittar crawlers den sällan.
Varje rutt behöver sin egen titel, metabeskrivning, kanonisk länk och Open Graph-taggar, genererade på servern när det är möjligt. Vår sida om metataggar listar vad som ska sättas. Behåll en enda kanonisk tagg: Google rekommenderar att sätta den i HTML:en, och om JavaScript ändrar den får resultatet inte motsäga originalet.
Saknade produkter och raderade artiklar får inte svara 200 med ett felmeddelande. Med SSR returnerar du en riktig 404 eller 410 från servern. I en ren SPA godkänner Google två reservlösningar: omdirigera med JavaScript till en URL som returnerar 404, eller lägg till en noindex-metatagg för robotar på felvyn.
Rubriker, brödtext, priser, FAQ och JSON-LD-strukturerad data bör finnas i serverns svar, inte laddas efter ett klick eller en scroll. Innehåll bakom flikar som renderas vid behov, eller bakom knappar för „visa fler”, missas ofta.
Dela upp paketet per rutt, skjut upp det som inte behövs för första vyn och undvik att rendera om hela sidan vid hydrering. Observera att Chrome-fältdata (CrUX) tillskriver mätvärdena för navigering i appen till den URL som besökaren laddade först, så testa dina viktigaste mallar var för sig.
Jämför tre versioner av varje viktig mall: den råa HTML:en (visa källkoden eller hämta sidan med ett kommandoradsverktyg), Googles renderade HTML (URL-kontroll, sedan „Visa genomsökt sida”) och sidan med JavaScript avstängt. Om huvudtexten bara syns i den andra versionen ser inte AI-crawlers den. Vår kostnadsfria granskning av genomsökbarhet för AI och kontroll av indexerbarhet gör dessa kontroller på valfri URL.
| Symptom | Trolig orsak | Åtgärd |
|---|---|---|
| Search Console visar många sidor med „Genomsökt, för närvarande inte indexerad” | Tom eller nästan identisk HTML före rendering | SSR, SSG eller förrendering för publika rutter |
Varje resultat i en site:-sökning visar samma titel | Titeln uppdateras inte per rutt | Hantering av head per rutt på servern |
| Bara startsidan är indexerad | Hashrutter eller länkar utan href | History API och riktiga <a>-länkar |
| Felsidor syns i indexet | Mjuka 404-fel (status 200 överallt) | 404 från servern, eller JavaScript-omdirigering till en 404-URL, eller noindex |
| Rankas på Google men citeras aldrig av ChatGPT eller Perplexity | Innehåll som bara syns efter körning av JavaScript | Innehåll i det första HTML-svaret |
| Gammalt innehåll visas i Googles renderade vy | Cachade JavaScript- eller CSS-filer | Filnamn med fingeravtryck |
I åratal var rendering i klienten en hanterbar risk, eftersom Google så småningom renderade JavaScript. Eftersom en del av upptäckten nu sker i ChatGPT, Claude, Gemini och Perplexity har kostnaden förändrats: en sida som bara finns efter att JavaScript körts saknas i de källor de flesta assistenter kan läsa. Genomsökbarhet i den råa HTML:en har blivit det första villkoret för generativ motormarknadsföring.
När HTML:en är komplett gäller resten de vanliga reglerna: sidor som besvarar en fråga tydligt, med källbelagda fakta och strukturerad data, är de som assistenterna citerar. Soranks GEO-optimering hjälper dig att skriva och publicera det innehållet, och spårning av AI-omnämnanden visar om ChatGPT, Claude och Gemini citerar ditt varumärke efter renderingsfixen.
Den viktigaste regeln i SEO för single page-applikationer får plats i en mening: det du vill att Google ska rangordna och AI ska citera måste finnas i den HTML som servern skickar. Behåll SPA:n för den inloggade upplevelsen, rendera publika sidor på servern eller vid bygget, och testa sedan vad robotarna får före och efter varje release.
En single page-applikation straffas inte av Google, men dess standardupplägg, rendering i klienten med ett enda HTML-skal, gör indexeringen långsammare och mindre pålitlig. De flesta AI-crawlers kör inte JavaScript, så de ser en tom sida. Med serverrendering, statisk generering eller förrendering på publika rutter kan en SPA rankas lika bra som vilken klassisk webbplats som helst.
React i sig är neutralt: det som spelar roll är var HTML:en skapas. En React-app som renderas enbart i webbläsaren har de vanliga problemen med SPA:er, medan samma komponenter renderade på servern med Next.js eller React Router i ramverksläge levererar komplett HTML till crawlers. Välj ett ramverk som stöder serverrendering eller statisk rendering för de sidor du vill ha indexerade.
Bara som en tillfällig åtgärd. Google beskriver dynamisk rendering som en lösning för stunden och inte en rekommenderad lösning, eftersom det att underhålla en separat version för robotar medför kostnad och risk. Serverrendering, statisk generering eller förrendering vid bygget ger samma HTML till alla besökare och är de långsiktiga lösningarna.
DE TESTADE SORANK FÖRE DIG
