If your page builds its content with JavaScript in the browser, the crawlers behind ChatGPT, Claude and Perplexity see an empty shell. They download the HTML, they may even download your bundles, but they don't execute them. Googlebot does run JavaScript, which is why this goes unnoticed for so long: your page can rank fine in Google and still doesn't exist for a big part of AI search. The demo above puts both readers side by side, a real browser on the left and what a no-JS crawler gets on the right, with the same HTML going to both.
What the crawlers actually do
The clearest data I've found is Vercel's study of crawler traffic on its network, published in December 2024. Their conclusion is blunt: "none of the major AI crawlers currently render JavaScript". The list they checked includes OpenAI's GPTBot, OAI-SearchBot and ChatGPT-User, Anthropic's ClaudeBot, Meta-ExternalAgent, Bytespider and PerplexityBot. They also note that ChatGPT's and Claude's crawlers do fetch JavaScript files, they just don't run them, so client-side rendered content is invisible to them.
The exceptions are the crawlers that sit on top of a browser. According to the same study, Gemini uses Googlebot's infrastructure, which gives it full JavaScript rendering, and AppleBot renders through a browser-based crawler too.
And these aren't fringe bots. In the month Vercel measured, GPTBot made 569 million fetches and Claude's crawlers 370 million. Together with AppleBot and PerplexityBot that's close to 1.3 billion requests, a little over 28% of Googlebot's volume.
Google's own documentation says the same thing from the other side. Googlebot processes JavaScript apps in three phases (crawling, rendering, indexing) and renders with an evergreen Chromium, but pages can sit in the rendering queue for a while. The docs still recommend server-side rendering or pre-rendering, partly because "not all bots can run JavaScript".
Seeing your page the way they do
You don't need a special tool to look at your page the way these crawlers do. A crawler that doesn't run JavaScript gets the same bytes curl gets:
# The HTML as it arrives, before any script runs
curl -s https://richi.codes/article/2026-11-05-ai-crawlers-dont-run-your-javascript | grep -o '<h1[^>]*>[^<]*</h1>'
If your headline, your text or your structured data aren't in that output, an AI crawler won't find them either.
The demo does the same thing in the browser with DOMParser. MDN describes it plainly: when you parse with text/html, <script> elements are marked as non-executable and event handlers don't run, so the resulting document is "essentially inert". That's a decent stand-in for a crawler that only reads HTML:
function readAsBot(html) {
const doc = new DOMParser().parseFromString(html, 'text/html'); // scripts are never run
const types = [...doc.querySelectorAll('script[type="application/ld+json"]')].map((s) => {
try { return JSON.parse(s.textContent)['@type']; } catch { return '?'; }
});
doc.querySelectorAll('script, style, template').forEach((n) => n.remove());
return {
title: doc.title.trim(),
description: doc.querySelector('meta[name="description"]')?.content || '',
h1: doc.querySelector('h1')?.textContent.trim() || '',
jsonLd: types,
links: doc.querySelectorAll('a[href]').length,
text: (doc.body?.textContent || '').replace(/\s+/g, ' ').trim()
};
}
Two details matter. The JSON-LD has to be read before the <script> elements are removed, because structured data lives inside a script tag even though it's data rather than code. And the browser pane gets its numbers from a nested sandboxed iframe that runs the same HTML for real and reports document.body.innerText back with postMessage.
Try the three presets:
- Client-rendered SPA: an empty
<div id="root">plus a script. The browser shows a full product page. The crawler gets a generic title, no description, no<h1>, no JSON-LD, no links, and only the<noscript>message. - Server-rendered: the same page with everything in the HTML. Both readers agree.
- SSR + JS widget: the main content comes from the server and only the reviews box is filled in by a script. The crawler loses exactly that box, which is fine if the reviews aren't what you want cited.
You can also open the editor and paste your own page's source (view-source, not the DOM inspector, which shows the page after JavaScript has run).
Gotchas
- Structured data injected by JavaScript doesn't count for these bots. A tag manager that adds JSON-LD on the client gives Googlebot something and the others nothing. Vercel notes that data present in the initial HTML, JSON included, can still be picked up.
- Hydration is fine. Server rendering plus JavaScript that takes over afterwards is exactly the "server-rendered" case: the content is already in the HTML. This site's articles are generated with Next.js
getStaticProps, and thecurlabove returns the headline straight from the HTML. - Meta tags set on the client don't help either. A
document.titlechanged in auseEffectis the "App" title the crawler sees in the first preset. DOMParseris an approximation. It shows what's in the HTML; it doesn't know about robots.txt rules, redirects or how each company processes the text afterwards.
What to keep an eye on
The Vercel numbers are from late 2024, and crawlers change. If an AI company starts rendering JavaScript, it will most likely say so in its crawler documentation, so that's where to look. Until then, the safe rule is the one both Google and Vercel give: put your main content, metadata and navigation in the HTML your server sends, and keep client-side rendering for the extras.