Most AI crawlers don't run JavaScript. They fetch your raw HTML and read whatever is in it — nothing more. So if your headline, your copy and your key facts only appear after the page loads and hydrates in a browser, then ChatGPT, Claude, Gemini and Perplexity see an empty shell. You can be fully allowed in robots.txt, rank fine on Google, and still be invisible to AI assistants — because being reachableisn't the same as being readable.

What “rendering” actually means

When a browser loads a modern web app, two things happen. First it downloads the initial HTML the server sent. Then it runs JavaScript that fetches data and builds the rest of the page — the part you actually read. For a human with a browser, this is invisible and fast. For a crawler that doesn't run that second step, the page is whatever the server sent in step one.

A purely client-rendered app sends something like this as its initial HTML:

<body>
  <div id="root"></div>
  <script src="/static/app.js"></script>
</body>

A browser turns that into your full site. A crawler that skips the JavaScript sees exactly what's above: an empty div and a script tag.No headline, no product description, no pricing, no proof. From the model's point of view, the page is blank.

Why AI crawlers skip JavaScript

Running a full browser to render every page is slow and expensive. At the scale these crawlers operate, most of them simply request the HTML and move on. GPTBot, OAI-SearchBot, ClaudeBot and PerplexityBot are best understood as readers of raw HTML, not as headless browsers. Some on-demand fetchers may do more, but you should not build your visibility on the assumption that they will.

The safe mental model: assume the AI crawler sees your View Source, not your rendered page.

This is the part that catches teams off guard, because it contradicts their experience of the site. The page looks perfect to them — they have a browser. The bot doesn't.

“But we rank on Google”

A fair objection. Googlebot doesrender JavaScript — it queues your page and runs a headless browser on a second pass, so a client-rendered site can still get indexed. That success creates a false sense of safety. AI answer engines are a different audience with different crawlers, and they don't hand you that second pass. Ranking on Google tells you Googlebot's renderer worked; it tells you nothing about whether ChatGPT can read you. They're separate problems, and right now AEO is the one most sites haven't solved.

How to check what the bots actually see

Don't trust the rendered page in your browser — that's the one view that hides the problem. Look at the raw HTML instead. Three ways, easiest first:

  1. View Source, not Inspect.Right-click → “View Page Source” (or view-source:https://yourdomain.com). This is the HTML the server sent — the bot's view. The DevTools Inspector shows the rendered DOM after JS, which is not what the bot gets. Use View Source.
  2. Fetch it as the bot. From a terminal:
curl -A "GPTBot" https://yourdomain.com | less
# Search for your headline or a sentence of body copy.
# Not there? The bot can't see it either.
  1. Disable JavaScript and reload.In DevTools (Command/Ctrl-Shift-P → “Disable JavaScript”), reload the page. What remains is roughly what a non-rendering crawler reads. If the page goes blank or loses its main content, you've found the problem.

The tell is simple: search the raw HTML for a sentence of your actual content. If it isn't there, neither is your visibility. AEOScan automates this — it reads your site the way an AI crawler does and flags content that only exists after JavaScript runs, alongside crawler-access and structured-data checks.

What tends to break

  • Client-only single-page apps. A create-react-app or Vite SPA with no server rendering ships the empty-shell HTML above.
  • Content loaded with client-side fetches. Even on an otherwise-rendered page, a section populated by a useEffect data fetch won't be in the HTML the bot reads.
  • Content hidden behind interaction.Copy that only appears after a click, a tab switch, or an “accordion” expand is invisible to a crawler that never interacts.
  • Text baked into images or canvas. If your value proposition lives inside a hero image or a canvas element, there is no text for the model to read.

How to fix it

The goal is simple: your content must exist in the initial HTML the server sends, before any JavaScript runs. You don't have to drop your framework — you have to render it on the server.

  • Server-side rendering (SSR). The server builds the full HTML for each request. Next.js, Nuxt, SvelteKit, Remix and Astro all do this. The bot gets a complete page; the browser hydrates it into an app.
  • Static generation (SSG) / ISR.For content that doesn't change per request — marketing pages, blog posts, docs — prerender to static HTML at build time (and revalidate on a schedule). Fastest, most reliable, and trivially crawlable.
  • Prerendering for an existing SPA.If you can't migrate, a prerender step (or a prerendering service) generates static HTML snapshots of each route so crawlers get real content without you rewriting the app.
  • Progressive enhancement. Put the core content and text in the HTML, then layer interactivity on top — rather than building the content with JavaScript in the first place.

After you ship the fix, re-run the checks above. The headline and key copy should now appear in View Source and in the curl output, with JavaScript disabled.

Where this fits

JS rendering is the second of the three things that decide AI visibility, and the one most teams miss:

  1. Let the crawlers in. Allow the AI bots in robots.txt and at your CDN/WAF — otherwise nothing else matters. We covered crawler access here.
  2. Render content in raw HTML. This article — make sure what you want read is in the HTML, not built by JavaScript.
  3. Add structured data and an llms.txt map. Help assistants identify what you are and which pages matter. More on llms.txt here.

Fix them in that order. An llms.txt map is pointless if the page it points to is an empty shell, and clean JSON-LD can't rescue content the crawler never received.

Frequently asked questions

Do AI crawlers run JavaScript?

Mostly no. The crawlers that feed ChatGPT, Claude and Perplexity (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot) fetch your raw HTML and generally do not execute JavaScript or wait for client-side rendering. So if your content only appears after the page hydrates in a browser, those bots see an empty shell. Assume they read HTML, not a rendered page.

But Googlebot renders JavaScript — isn't that enough?

Googlebot does render JS (on a delay, via a second pass), which is why a client-rendered site can still rank in Google. But AI answer engines are a different audience with different crawlers, and they don't give you that second pass. Optimizing for Googlebot's renderer is not the same as being visible to ChatGPT. Treat them as separate problems.

How do I check what the bots actually see?

View the raw HTML, not the rendered page. Use 'View Source' (not 'Inspect'), or run curl -A "GPTBot" https://yourdomain.com and read the response. If your headline, body copy and key facts aren't in that HTML, the bot can't see them. Disabling JavaScript in your browser is a quick visual version of the same test.

Does this mean I can't use React, Vue or a single-page app?

You can — you just need the content to exist in the initial HTML. Server-side rendering (SSR), static generation (SSG) or prerendering all do this while keeping your framework. The anti-pattern is shipping an empty <div id="root"></div> and building the whole page client-side. Render on the server, hydrate on the client.

Will fixing this also help my SEO?

Usually yes. Server-rendered HTML is faster to first paint, more reliable for every crawler (Google included), and removes the render-delay risk. Making your content readable without JavaScript is good for traditional SEO and for AEO at the same time.