All posts

Why ChatGPT can't see your site: the JavaScript rendering problem

July 12, 2026 · 9 min · Spike research

You shipped a beautiful React app. It ranks on Google. Your Core Web Vitals are green. And ChatGPT can't see a single word of it.

That's not a bug in your code — it's a mismatch between how your site renders and how AI crawlers read. Most AI crawlers do not run JavaScript. They fetch your raw HTML over a plain HTTP request and read whatever text is already in it. If your headline, your product copy, and your pricing only appear after the browser downloads and executes a bundle — the hallmark of client-side rendering — then to an AI assistant your page is close to blank.

This is the single most common, most invisible reason a modern site gets ignored by AI answer engines. It's also one of the most fixable. Here's exactly what's happening, how to test your own site in thirty seconds, and how to fix it on whatever stack you're on.

Which AI crawlers run JavaScript? (almost none)

When Googlebot visits a client-rendered page, it does something almost no other crawler on earth does at scale: it queues the page, spins up a headless Chromium, executes your JavaScript, waits for the DOM to settle, and indexes the rendered result. That rendering pipeline is expensive. Google runs one anyway.

The bots feeding AI assistants do not. They fetch the bytes and parse them — no headless browser, no JavaScript execution, no waiting for hydration. Whatever is in the initial HTML response is the entire universe of text they get.

  • GPTBot (OpenAI's crawler) — raw HTML, no JavaScript
  • OAI-SearchBot (ChatGPT search) — raw HTML, no JavaScript
  • ClaudeBot (Anthropic) — raw HTML, no JavaScript
  • PerplexityBot (Perplexity) — raw HTML, no JavaScript
  • Googlebot — renders JavaScript

So you can sit on page one of Google and be functionally invisible to every major AI assistant at the same time. The two systems read the web differently, and optimizing for one tells you almost nothing about the other. This catches teams off guard precisely because the site works — it works for humans, whose browsers run the JavaScript, and it works for Google, which renders it. The failure is completely silent.

How to tell if your site is invisible to AI

You don't need a tool to get a first read. You need curl, which fetches raw HTML exactly the way an AI crawler does — no browser, no JavaScript.

First, check whether your main headline actually exists in the raw response:

curl -s https://yoursite.com | grep -o "<h1[^>]*>[^<]*</h1>"

If that prints your real H1, good. If it prints nothing — or a generic app-shell string like Loading... — your headline is being injected by JavaScript that the crawler never runs.

Now measure how much readable text is in the raw HTML:

curl -s https://yoursite.com | sed 's/<[^>]*>/ /g' | tr -s ' ' | wc -w

Compare that number to what a human sees. A real content page should have hundreds to low thousands of words in the raw HTML. If curl reports 20, 40, 80 words while the rendered page clearly has far more, that gap is your JavaScript rendering problem in a single number. The crawler gets the 40. The 40 is all ChatGPT knows about you.

This is exactly the check Spike's free scan automates. It fetches your page as a raw-HTML crawler and again as a real browser, diffs the two word-for-word, and if the raw version is missing the bulk of your content it flags your site as invisible to AI crawlers — with the actual numbers, not a vague warning. You can see the shape of that output in a sample report.

How to fix it: server-side rendering and pre-rendering

The fix is always the same idea: get your content into the HTML before it leaves the server, so there's nothing left to execute. Three ways to do that.

  • Server-side rendering (SSR) — the server runs your components per request and returns finished HTML. Best for pages that change often or are personalized.
  • Static site generation (SSG) / pre-rendering — you render to HTML once at build time and serve the static file. Best for content that's the same for everyone: marketing pages, docs, blog posts. Fastest, cheapest, and perfectly legible to every crawler.
  • Prerender proxy — middleware detects crawlers and serves them a pre-rendered snapshot while humans still get the SPA. A retrofit, not a rebuild — useful when you can't touch the app quickly.

For the pages that decide your AI visibility — the ones describing what you do, who you're for, and what you cost — SSG is almost always the right answer. There's no reason your homepage copy should require a JavaScript runtime to be read.

Framework-specific fixes

Next.js

With the App Router, Server Components are the default — a page ships server-rendered HTML unless you opt out. The trap is "use client": the moment you put it at the top of a content page, that component and everything under it renders on the client, and its text can disappear from the raw HTML. Keep "use client" on the leaf interactive bits — a form, a carousel, a dropdown — and let the content around them stay on the server.

// A Server Component by default — its HTML ships fully rendered
export default async function PricingPage() {
  const plans = await getPlans(); // runs on the server
  return <PricingTable plans={plans} />;
}

For dynamic routes, use generateStaticParams to pre-render each path at build time. On the Pages Router, the equivalents are getStaticProps (static) and getServerSideProps (per request).

React SPA (Vite, Create React App)

A pure SPA ships an empty root div and a script tag — the worst case for AI crawlers. Two paths. First, pre-render at build with a tool like react-snap, which crawls your routes in headless Chromium and writes static HTML snapshots; fine for small, mostly-static sites. Second, for anything real, move the content surface to a framework with rendering built in — Next.js, Remix, or Astro. Astro is purpose-built for content and ships zero JavaScript by default. If a migration is off the table, a prerender proxy is the stopgap.

Vue

Same disease, same cure: a client-rendered Vue app is invisible for exactly the same reason. Move to Nuxt and enable SSR, or run nuxt generate for a fully static build. Nuxt renders your components to HTML on the server so the content is in the initial response instead of behind a bundle.

WordPress, Shopify, Webflow

These are server-rendered by default, so you're usually fine out of the box — the HTML ships with your content in it. The exceptions worth checking: heavy "headless" setups, page builders or Shopify sections that inject copy via client-side script, and single-page-app themes. Run the same curl test; if the words are there, you're good.

If you'd rather not diagnose the stack by hand, Spike does it for you. The scan detects which framework you're on and which pages are affected, and the Fix Pack ships the exact pre-rendering change for your setup — the generateStaticParams route, the react-snap config, the Nuxt SSR flag — not generic advice.

Rendering is the foundation, not the finish line. Once your content is actually in the HTML, the next questions are what it says and how it's structured — the subject of our Answer Engine Optimization guide and the breakdown of Generative Engine Optimization vs SEO. And once you're visible, the 7 fixes that make AI cite you covers the schema, llms.txt, and freshness signals that turn a readable page into a cited one.

Frequently asked questions

Do AI crawlers run JavaScript?

No. The major AI crawlers — GPTBot, OAI-SearchBot, ClaudeBot, and PerplexityBot — fetch raw HTML and do not execute JavaScript. If your content is injected client-side, they can't read it.

Does Google see my JavaScript site?

Usually yes. Googlebot runs a separate rendering step that executes JavaScript, so a client-rendered site can rank on Google even while it's invisible to AI assistants. Ranking on Google is not evidence that ChatGPT can see you.

How do I make my SPA visible to ChatGPT?

Get your content into the raw HTML. Pre-render your routes at build time (react-snap for a small SPA), adopt a framework that renders on the server (Next.js, Remix, Astro, or Nuxt for Vue), or put a prerender proxy in front of crawlers. Then confirm with the curl test above.

How do I test what an AI crawler sees?

Run curl -s https://yoursite.com and read the output — that is the raw HTML an AI crawler gets. If your headline and body copy aren't in it, neither is your visibility. Or run a free scan that diffs the raw HTML against the rendered page for you.

Your site either has its content in the HTML or it doesn't, and right now you probably don't know which. It takes thirty seconds to find out. Scan your site free and see exactly what ChatGPT sees when it looks at you.

Wondering what AI says about your brand?

Run the free multi-engine scan — score, competitors, and the full Fix Pack in about 90 seconds.

Scan my visibility