Google renders most JavaScript pages successfully but on a delay, sometimes hours, sometimes weeks. AI crawlers like GPTBot and ClaudeBot render almost none of it. The fix is server-side rendering or static generation for anything you want ranked or cited, real anchor-tag links, and a verification loop that checks rendered HTML, not just source.
Single-page applications are still one of the most common technical SEO failure points in 2026. The frameworks have matured, React Server Components and Next.js App Router have made server rendering the default in a lot of new projects, but a huge number of production SPAs still ship as client-side-rendered shells with content injected after the fact. That gap between what a browser shows a user and what a crawler sees is where rankings and AI citations quietly die.
The stakes are higher than they were three years ago. ChatGPT now has 900 million weekly active users as of February 2026 (TechCrunch), and Google's AI Mode has surpassed roughly 1 billion users this year. Neither ChatGPT's crawler nor most other AI bots execute JavaScript at all, so a page that depends on client-side rendering to show its content is functionally invisible to the fastest-growing discovery channel in search. Google itself, which does render JavaScript, still queues that rendering work separately from crawling, which means content can sit unindexed for days after Googlebot's first visit.
This piece covers how Google and AI crawlers actually process JavaScript in 2026, the rendering strategies that hold up, and a step-by-step process for auditing and fixing a SPA so it's crawlable by every bot that matters, not just the one running headless Chrome.
How Google Actually Renders JavaScript Today
Google's indexing pipeline for JavaScript pages runs in two separate passes, and understanding the gap between them explains most SPA indexing problems.
Wave one: the raw HTML crawl. Googlebot requests the URL and reads whatever comes back in the initial HTTP response, no script execution involved. If your SPA serves an empty <div id="root"></div> shell, this is all Google has to work with until wave two runs. Titles, meta descriptions, and any content that only exists after JavaScript runs are absent at this stage.
Wave two: the Web Rendering Service. Google queues the page for rendering with a headless Chromium instance, the same rendering engine that powers Chrome itself, then re-crawls the rendered DOM and indexes what it finds. This is where your client-side content actually gets read.
The critical detail is the delay between the two waves. A joint study by Vercel and MERJ, analyzing more than 100,000 Googlebot fetches and over 37,000 rendered pages matched against server logs, found the render happened within 10 seconds at the 50th percentile, but stretched to roughly 3 hours at the 90th percentile and 18 hours at the 99th percentile (Vercel). URLs with query strings fared worse, a 75th-percentile delay of roughly 31 minutes versus 22 seconds for clean URLs. In practice, smaller or lower-authority domains anecdotally see rendering queues stretch well beyond those percentiles, since Google's own documentation confirms rendering resources aren't allocated evenly across every site.
That means the same SPA can behave completely differently depending on domain authority. A well-established site might see JavaScript content indexed within minutes. A newer or lower-traffic site can wait a week or more for the same content to surface, during which competitors with server-rendered pages are already ranking.
Google's two-wave rendering process: raw HTML is crawled instantly, but JavaScript content waits in a separate, often-delayed render queue.
Why Dynamic Rendering Is No Longer the Answer
For years, the standard workaround was dynamic rendering: detect the user agent, serve a prerendered HTML snapshot to bots and the full JavaScript app to humans. Google has walked back its recommendation of this approach. The technique depends on fragile user-agent sniffing that has to be manually maintained every time a new crawler shows up, and it risks serving meaningfully different content to bots versus users, which edges toward cloaking even when unintentional (Search Engine Land).
Google's current, explicit guidance favors server-side rendering, static generation, or hydration as the default architecture, with dynamic rendering treated as a stopgap for legacy migrations, not a pattern to build new on (Google Search Central).
Why AI Crawlers Change the Calculus Entirely
If Google were the only bot that mattered, a several-hour or even several-day rendering delay might be tolerable. It isn't the only bot that matters anymore, and this is the part of JavaScript SEO teams most often get wrong in 2026.
GPTBot, ClaudeBot, and PerplexityBot do not run a browser engine. They send a standard HTTP request, read the raw HTML that comes back, and move on, no waiting for scripts, no second pass, no rendering queue. Vercel's analysis of over 569 million GPTBot requests and 370 million ClaudeBot requests found zero evidence of JavaScript execution from either crawler, even though GPTBot downloads .js files in roughly 11.5% of requests and ClaudeBot in roughly 23.8%, apparently without running them (Vercel). Google-Extended is the one exception among AI-related crawlers, since it inherits Googlebot's rendering infrastructure.
The practical consequence: a client-side-rendered SPA can rank respectably in Google's traditional results while being completely absent from ChatGPT, Perplexity, and Claude's answers, because those systems never see anything past the empty shell. Given that AI Overviews now cut position-one organic CTR by 58% and zero-click search behavior has risen from 54% to 72% (Ahrefs), losing eligibility for AI citations is a direct hit to the traffic sources that are actually growing. This is the failure mode SEOguru's GEO scoring is built to catch, flagging pages where the rendered-vs-raw-HTML gap is large enough to make a page invisible to non-rendering crawlers, before it becomes a citation problem.
There's a compounding effect too. The Princeton/Georgia Tech GEO study presented at KDD 2024 found that adding statistics, quotations, and cited sources to a page can lift its visibility in AI-generated answers by up to 115% for previously low-ranked pages (arXiv). None of that lift is reachable if the content isn't present in the raw HTML that AI crawlers actually read.
Rendering Strategy Comparison: Picking the Right Architecture
Not every page on a SPA needs the same rendering approach. The table below is the framework we walk clients through when auditing a JavaScript-heavy site.
| Strategy | What crawlers see | Best for | Rendering delay risk | AI crawler visibility |
|---|---|---|---|---|
| Client-side rendering (CSR) only | Empty shell until JS executes | Logged-in dashboards, internal tools | High, depends on WRS queue | None, invisible to non-rendering bots |
| Server-side rendering (SSR) | Fully populated HTML on first request | Marketing pages, PLPs, blog, anything needing rankings | None, content is in wave-one HTML | Full |
| Static site generation (SSG) | Pre-built HTML served from CDN | Docs, blogs, marketing pages with infrequent updates | None | Full |
| Incremental static regeneration (ISR) | Static HTML that rebuilds on a schedule or trigger | Product catalogs, pages that update but not per-request | None | Full |
| Dynamic rendering (legacy) | Prerendered snapshot to bots, JS app to users | Migrating a legacy SPA you can't rebuild yet | Low for Google, but fragile and discouraged | Depends entirely on bot list maintenance |
| Hydration (SSR + client takeover) | HTML on load, JS attaches interactivity after | Apps that need SEO plus rich interactivity | None for initial content | Full for initial content |
The rule of thumb we give clients: anything you want to rank or be cited by an AI engine needs to exist as real HTML on the first response, no exceptions. Client-side-only rendering should be reserved for functionality that has no organic search intent behind it at all, like an authenticated settings panel.
Rendering strategy determines whether Googlebot and non-rendering AI crawlers see your content at all, not just how fast.
Step-by-Step: Auditing Your SPA for Crawlability
Here's the sequence we run on every JavaScript-heavy site before recommending fixes.
- Fetch the raw HTML with a tool that doesn't execute JS. Use
curlorwgetagainst the live URL. If your title tag, meta description, H1, and primary body content aren't in that response, wave one has nothing to work with. - Compare against the rendered DOM. Use Search Console's URL Inspection tool and click "View Crawled Page" to see the rendered HTML Google actually indexed. A large gap between raw and rendered HTML is your red flag.
- Check Coverage and indexing status in GSC. Look for pages stuck in "Discovered, currently not indexed" or "Crawled, currently not indexed," both common symptoms of rendering-queue delays. SEOguru's Search Console integration surfaces this at the URL level so you're not spot-checking pages one at a time.
- Audit internal links for real
<a href>tags. Anything wired toonClickhandlers or router push events without a backinghrefattribute is invisible to link-based crawling, for Google and every AI bot. - Test with JavaScript disabled. Load the page with JS turned off. What remains is close to what AI crawlers see. If nav, main content, and links disappear, so does your AI visibility.
- Check canonical tag behavior pre- and post-render. HTTP Archive data shows roughly 2-3% of pages have canonical URLs that change after JavaScript execution, sending conflicting signals about which URL should rank (Search Engine Land).
- Verify sitemap coverage matches rendered routes. A stale sitemap that excludes client-side-generated routes leaves whole sections undiscoverable.
- Measure Time to First Byte and Largest Contentful Paint. Slow server response times push pages further back in the rendering queue and hurt Core Web Vitals, which still factor into crawl prioritization.
Common Implementation Mistakes
- Content behind infinite scroll with no paginated URL fallback. Crawlers can't scroll. Without a
?page=2equivalent with a real link, that content doesn't exist for indexing. - Router-based navigation using
<div onClick>instead of<a href>. This breaks link equity flow and makes pages unreachable by crawl alone, forcing total reliance on the sitemap. - Meta tags injected via JavaScript with no SSR fallback. Titles and descriptions that only render client-side get missed by wave-one crawls and by any bot that doesn't render JS.
- Blocking JS or CSS files in robots.txt. A legacy habit from 2015-era SEO advice that actively prevents Google's renderer from seeing your page correctly today.
- Serving a 200 status code for error states. Soft 404s inside SPAs, where a broken route still returns HTTP 200, waste crawl budget and confuse indexing signals.
- Relying on client-side redirects instead of HTTP 301s. A
window.locationredirect only fires after JS executes, which non-rendering crawlers never see.
Making Internal Linking Work Inside a SPA
Internal links are how crawlers discover your pages in the first place, and SPAs are notorious for breaking this in subtle ways. A component that looks like a link, styled and clickable, but implemented as a <button> or <span> with a JavaScript click handler, carries zero crawl or link-equity value.
The fix is mechanical: every navigational element that leads to another indexable page needs an actual <a href="/path"> tag, even in frameworks using client-side routing like React Router or Vue Router. Both support rendering real anchor tags while still intercepting clicks for client-side navigation, so there's no functional tradeoff, it's purely a matter of using the right component.
Once the links exist as real HTML, the next question is whether they're structured well. A flat SPA with every route hanging off the homepage buries deep pages and starves them of internal link equity. See our breakdowns on fixing crawl depth and orphan pages and internal linking at scale if your SPA has grown past a few dozen routes and needs a real linking architecture, not ad hoc links added page by page.
Checklist for SPA internal linking specifically:
- [ ] Every nav menu item renders as
<a href>in the raw HTML response - [ ] Breadcrumbs use real anchor tags, not JS-only click handlers
- [ ] Pagination controls have unique, crawlable URLs (
?page=2, not a state variable) - [ ] Related-content and "you might also like" modules render server-side
- [ ] Footer links are present in raw HTML, not injected after hydration
- [ ] No critical page is reachable only through a search box or filter UI
- [ ] XML sitemap includes every route the client-side router can generate
Schema and Metadata in a JavaScript Context
Structured data still matters in a JavaScript-rendered site, just not for the reason it used to. Google retired HowTo rich results in 2023 and removed FAQ rich results from search on May 7, 2026, so neither schema type produces SERP enhancements anymore. What they still do is give AI systems a clean, unambiguous data structure to extract when summarizing or citing your content, which matters more now than the rich-result snippet ever did.
For a SPA, the implementation detail that trips people up is timing: JSON-LD markup injected into the DOM after JavaScript executes is invisible to any crawler that doesn't render JS, which as covered above is most AI crawlers. Structured data has to be present in the server-rendered or statically generated HTML, not appended client-side. The same logic applies to Open Graph tags, canonical tags, and hreflang annotations.
A Practical Migration Path for Existing SPAs
Rebuilding an entire SPA on SSR is a big lift most teams can't do all at once. Here's the sequence that gets the highest-value pages fixed first without a full rewrite:
- Rank your routes by organic traffic and revenue potential. Product pages, category pages, and top blog posts go first. Internal tools and account pages go last, or never.
- Migrate the highest-priority template to SSR or SSG. In Next.js this means moving from client-only data fetching to
getServerSideProps,getStaticProps, or React Server Components. In Nuxt, enable universal rendering rather than SPA mode. - Verify with a raw-HTML fetch before and after. Confirm the content gap closed using the same
curltest from the audit process above. - Re-run GSC's URL Inspection tool to confirm Google's rendered view matches the live page, then request indexing for affected URLs.
- Move to the next template tier and repeat, tracking indexed-page count and impressions in Search Console.
- Retire dynamic rendering workarounds once SSR coverage is complete, since maintaining two systems in parallel adds risk without benefit.
This is the same phased approach we recommend in our broader technical SEO audit checklist: fix the highest-impact templates first, verify with real data, then move down the list rather than attempting a wholesale rebuild in one pass.
How This Fits Into a Broader Technical SEO Program
JavaScript rendering issues rarely show up in isolation. Sites with CSR-only architecture often also have crawl depth problems, because client-side routers make it easy to generate routes that never get a corresponding sitemap entry or internal link. They often have duplicate content issues too, since client-side state can produce multiple URLs for what's functionally the same page. Treating rendering, architecture, and linking as one coordinated program, not three separate tickets, is the difference between a fix that sticks and one that regresses in six months when a new component ships unchecked.
That's the operational gap SEOguru's technical SEO module is built around: continuous monitoring of indexation status per URL, not a one-time crawl, so a client-side-rendered component that breaks crawlability gets flagged before it costs you a quarter of rankings. Every fix still routes through an approval record before it publishes, which matters in JavaScript SEO work because rendering changes touch shared components and can affect far more pages than the one you meant to fix.
Frequently Asked Questions
Does Google index JavaScript-rendered content at all in 2026?
Yes. Google's Web Rendering Service executes JavaScript using headless Chromium and generally renders full pages successfully. The issue isn't capability, it's timing: rendering happens in a separate, delayed pass after the initial crawl, so content can take hours to weeks to get indexed.
How long does Google take to render JavaScript pages?
A Vercel and MERJ study of over 37,000 rendered pages found a median delay around 10 seconds, but the 90th percentile stretched to roughly 3 hours and the 99th percentile to about 18 hours. Lower-authority domains often see multi-day delays.
Can ChatGPT or Claude read content on my SPA?
Only if that content exists in the raw HTML response. GPTBot, ClaudeBot, and PerplexityBot don't execute JavaScript, so client-side-rendered content is invisible to them regardless of how well it performs in Google.
Is dynamic rendering still a valid SEO strategy?
Google no longer recommends it for new builds. It depends on fragile user-agent detection, requires ongoing maintenance as new crawlers appear, and risks serving different content to bots versus users. Use it only as a temporary bridge during migration.
Do I need to rebuild my entire SPA to fix JavaScript SEO issues?
No. Prioritize the templates that drive traffic and revenue, migrate those to server-side rendering or static generation first, verify with raw-HTML checks and Search Console, then work down the list.
Does structured data still help if my site is a SPA?
Yes, but only if it's rendered server-side. FAQ and HowTo rich results were removed from Google's SERP in 2026, but Article, Product, and Organization schema still help AI systems parse and cite content accurately, as long as the JSON-LD is present in the initial HTML.
What's the fastest way to check if my SPA has a rendering problem?
Fetch the page with curl and read the raw response. If your title, meta description, H1, and core content aren't there, compare against Search Console's "View Crawled Page" output to see what's missing.
Should I block JavaScript and CSS files in robots.txt to save crawl budget?
No. Blocking those resources prevents Google's renderer from seeing your page as intended, which can cause it to index a broken or incomplete version. This was outdated advice even before Google's 2026 documentation update removed the old JS-fallback warning.
Sources
- ChatGPT reaches 900M weekly active users
- Google AI Overviews reduce clicks, update on zero-click search
- GEO: Generative Engine Optimization study (Princeton/Georgia Tech, KDD 2024)
- Javascript SEO: How Google Crawls, Renders & Indexes JS
- The rise of the AI crawler
- No-JavaScript fallbacks in 2026: Less critical, still necessary
- Google no longer recommends using dynamic rendering for Google Search
- Understand JavaScript SEO Basics, Google Search Central