All articlesWhy Single-Page Apps Are Invisible to AI Crawlers (and How to Fix Client-Side Rendering)
9 min read

Why Single-Page Apps Are Invisible to AI Crawlers (and How to Fix Client-Side Rendering)

Most AI crawlers don't run JavaScript — so if your React or Vue app renders content client-side, ChatGPT, Perplexity, and Google AI Overviews may see nothing at all.

Single-page apps built with React, Vue, or Angular are often invisible to AI crawlers because most AI bots don't execute JavaScript before reading a page. They request the HTML, get an almost-empty <div id="root"></div>, and move on. If your content only appears after the browser runs a bundle of JavaScript, tools like ChatGPT Search, Perplexity, and Google AI Overviews may never see a single word of it.

This isn't a hypothetical edge case. It's the default state of a huge share of the modern web. If you've ever wondered why your React app ranks fine in Google but never gets cited in an AI Overview or shows up in a ChatGPT answer, this is almost always the reason.

Why can't AI crawlers read single-page apps?

Most AI crawlers are built to fetch pages fast and cheap, across billions of URLs. Running a full headless browser for every page — the kind of thing needed to execute React and wait for content to render — is expensive at that scale. So bots like GPTBot, PerplexityBot, and Google's AI-focused crawlers typically fetch raw HTML only. No JavaScript execution, no waiting for a fetch call to resolve, no hydration.

When your SPA ships an empty shell and lets client-side JavaScript build the actual page content, the crawler sees the shell. Nothing else. It's the digital equivalent of walking into a store and finding empty shelves, because the stock truck hasn't arrived yet — except the truck never arrives, because the crawler already left.

Googlebot is the one major exception, and even that comes with caveats. Google added a rendering step years ago that runs a version of Chromium against pages before indexing them. But that rendering happens on a delay (sometimes minutes, sometimes days), it's resource-limited, and it's specific to Google's own web index — not the AI answer engines layered on top of it, and not ChatGPT, Claude, or Perplexity at all.

What's actually happening under the hood with client-side rendering?

In a client-side rendered (CSR) app, the server sends a minimal HTML document — often just a script tag and an empty container — and the browser does the heavy lifting. JavaScript runs, fetches data from an API, and injects the real content into the DOM. This is great for building interactive apps. It's rough for anything that needs to be read by a bot that doesn't run JavaScript.

Here's roughly what a crawler sees when it requests a typical CSR page:

<html>
  <body>
    <div id="root"></div>
    <script src="/static/js/main.a1b2c3.js"></script>
  </body>
</html>

No headline. No product description. No pricing table. No FAQ content. Just a script tag pointing to a bundle the crawler isn't going to run. If you view-source on your own React app right now, there's a decent chance you'll see exactly this.

Is this worse for AI search than it is for Google SEO?

Yes, generally. Google has invested over a decade in rendering JavaScript for search indexing, and even then, plenty of SPA content gets indexed late, indexed partially, or missed entirely — Google's own documentation acknowledges a rendering queue that can delay indexing. AI crawlers are newer and leaner. Most of them skip rendering altogether.

That means a React app that ranks acceptably in classic Google search can be completely absent from an AI Overview answer, a Perplexity citation, or a ChatGPT Search result — because the AI layer is reading a different, unrendered version of your site. This is the core idea behind AI readability: it's not enough for content to exist in the browser. It has to exist in the raw response a crawler actually fetches.

I've looked at plenty of marketing sites built on React or Vue where the team assumed SEO was "handled" because Google indexed the homepage. Then they check an AI answer engine and find zero mentions of their brand for queries they should own. The gap is almost always client-side rendering.

How do I know if my React app isn't being indexed or read by AI?

Run a quick test before assuming anything. Three checks take under fifteen minutes combined:

  • View source, not DevTools. Right-click and choose "View Page Source" (not "Inspect"). If your headline, body copy, and key text aren't in that raw HTML, a non-rendering crawler won't see them either.
  • Disable JavaScript and reload. Chrome DevTools lets you disable JS entirely. If the page is blank or shows a spinner forever, that's your answer.
  • Fetch the URL with curl or a simple HTTP request. Compare what comes back to what you see in the browser. A large gap between the two is the smoking gun.

For a deeper walkthrough of testing specifically against ChatGPT's crawler behavior, there's a step-by-step guide on running free tests to check if ChatGPT can read your site that covers this exact scenario in more detail.

What actually fixes client-side rendering for AI crawlers?

The fix is to make sure meaningful content exists in the initial HTML response — before any JavaScript runs. There are a few solid ways to get there, and they're not all equal in effort or payoff.

Server-side rendering (SSR)

SSR renders your React or Vue components on the server for each request, sending fully-formed HTML to the browser (and to crawlers). Frameworks like Next.js, Nuxt, and SvelteKit handle this well out of the box. This is generally the strongest option for sites with frequently changing content — product pages, blog posts, anything with a CMS behind it.

Static site generation (SSG)

SSG pre-builds HTML at deploy time rather than per-request. It's faster to serve than SSR and just as crawler-friendly, but it's a better fit for content that doesn't change every few minutes — marketing pages, documentation, landing pages. Next.js, Gatsby, and Astro all support this pattern.

Dynamic rendering (the workaround, not the fix)

Dynamic rendering detects when a request comes from a bot and serves it a pre-rendered HTML snapshot, while regular users still get the full CSR app. Tools like Prerender.io or Rendertron handle this. It works, but it's a patch layered on top of the underlying problem, and it adds infrastructure you now have to maintain and keep in sync. I'd treat it as a stopgap while migrating to real SSR or SSG, not a permanent architecture.

Hybrid rendering

Most modern frameworks now support per-route rendering strategies — SSR for dynamic pages, static generation for stable ones, and CSR for behind-the-login-wall app sections that don't need to be crawled at all. This is usually the most practical setup for a real production site.

ApproachCrawler visibilityBest for
Pure CSRPoor — most AI crawlers see nothingInternal dashboards, logged-in app views
SSRStrong — full HTML on every requestFrequently updated content, e-commerce
SSGStrong — full HTML pre-builtMarketing pages, docs, blogs
Dynamic renderingModerate — depends on bot detection accuracyLegacy apps mid-migration

Does fixing rendering also help with WCAG accessibility?

It usually does, and the overlap is worth knowing about. Screen readers depend on the DOM being fully populated and structured with real semantic elements — not a blank container waiting on JavaScript. When you move from pure CSR to SSR or SSG, you're generally forced to think about real heading structure, real <a> tags instead of clickable <div> elements, and content that exists without a script running. Those are also core requirements under WCAG. The WebAIM Million study, which audits the home pages of the top one million sites each year, has consistently found that a large share of accessibility failures trace back to elements that aren't real semantic HTML — buttons built from <div> tags, missing labels, content that only appears after interaction. Fixing rendering for AI crawlers and fixing it for screen reader users turn out to be closer to the same project than most teams expect.

That's the exact intersection AccessKnight scans for — a single audit checks your site against 33 WCAG rules and scores whether AI crawlers can actually parse and cite your content, with code-level fixes for each failure rather than a JavaScript patch layered on top.

What about Next.js, Nuxt, and other meta-frameworks?

If you're starting a new project, this decision is mostly made for you. Next.js (React), Nuxt (Vue), SvelteKit (Svelte), and Remix all default to server rendering or static generation rather than pure client-side rendering. That's not an accident — the frameworks shifted this way specifically because the SEO and crawlability problems with pure SPAs became too costly to ignore.

If you're on an older Create React App project with no rendering strategy, migrating to Next.js is usually the highest-leverage fix available. It's not a small lift for a large app, but it solves the crawler visibility problem at the root instead of bolting on a workaround.

Key Takeaways

  • AI crawlers like GPTBot and PerplexityBot generally don't execute JavaScript, so pure client-side rendered content is invisible to them.
  • Googlebot renders JavaScript but on a delay, and that rendering doesn't extend to most AI answer engines.
  • Check your site by viewing page source (not DevTools) or disabling JavaScript to see what a non-rendering crawler actually gets.
  • Server-side rendering and static site generation are the durable fixes; dynamic rendering is a reasonable stopgap, not a destination.
  • Rendering fixes and WCAG accessibility fixes overlap more than most teams realize — both depend on real content existing in the DOM.

Frequently Asked Questions

Do I need to rewrite my whole React app to fix this?

Not necessarily. If you're using Create React App or a similar pure-CSR setup, migrating to Next.js (which supports React components) is usually less disruptive than a full rewrite, since you keep your component logic and mainly change how pages are rendered and routed.

Does Google index my SPA even without SSR?

Sometimes, but slowly and inconsistently. Google's renderer runs on a queue that can take minutes to days, and complex apps with heavy API calls or client-side routing often get partially indexed at best. Relying on it is risky for anything business-critical.

Will AI crawlers ever start rendering JavaScript like Google does?

It's possible some will add limited rendering over time, but there's no indication most AI retrieval crawlers plan to run full headless browsers at scale soon — it's expensive, and most publishers can fix the problem on their end far more cheaply than crawlers can fix it on theirs.

Is dynamic rendering (Prerender.io style) a safe long-term solution?

It's workable but fragile. You're maintaining a separate rendering pipeline that has to detect bots correctly and stay in sync with your live app. Most teams that adopt it end up migrating to SSR or SSG within a year or two anyway.

How can I tell if AI tools like ChatGPT are actually citing my site right now?

Ask ChatGPT Search or Perplexity a question your page should answer and see whether your domain shows up in the citations. If it doesn't, and your rendering is client-side only, that's usually the first thing to check.

Where to go from here

If you manage a React, Vue, or Angular app and you're not sure which category it falls into, don't guess. View source on your homepage right now and see what's actually in the raw HTML. If it's mostly empty, that's your starting point — not a redesign, just a rendering strategy fix on the routes that matter most for search and AI visibility. Running a free scan against your live URL will show you exactly which pages are readable to AI crawlers today and which ones are shipping blank shells.

Check your website's accessibility

Scan against all 33 WCAG 2.1 rules and get code-level fix suggestions — free.

Run a free scan

Keep reading