JavaScript Rendering: What AI Crawlers Never See

Of 3,218 B2B software homepages I crawled in late October 2026, 71 sent fewer than 50 words of text before JavaScript ran. That is 2.2%, from a study of 3,877 companies.

Most sites pass this check. The ones that fail, fail completely.

That is the shape of the problem. It is not a widespread crisis. It is not worth a quarter of budget. It is a yes or no question about your own site. Two minutes to answer, and expensive if the answer is yes.

Check your own site in two minutes

Request your homepage the way a crawler does and read what comes back.

curl -s https://yourdomain.com | wc -w
curl -s https://yourdomain.com | grep -i "your headline sentence"

The first command counts the words your server sends. The second looks for your primary claim inside them. Low count, no match? Your content arrives after JavaScript runs.

No terminal handy? Open the page, view source, and search for your headline. View source shows the server response. Inspect element shows the page after JavaScript, which is why it always looks fine and tells you nothing.

Which crawlers render JavaScript?

Two of them. Google’s and Apple’s.

Vercel and MERJ’s analysis found only two render. Gemini, on Googlebot infrastructure, and Applebot. GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot and PerplexityBot do not. ChatGPT’s crawler fetches JavaScript files on 11.5% of its requests and runs none of them. It reads them as inert text.

That split has a practical consequence worth holding on to. A claim that arrives after hydration can still reach a Google AI Overview, because Google renders. The same claim is invisible in ChatGPT, Claude and Perplexity. Write for the stricter reader and you cover both.

Server side, client side, and what SSR means

Three terms, one paragraph each, so the rest of this makes sense.

Server side rendering, or SSR. Your server builds the HTML and sends a complete page. The browser gets readable text immediately, and so does every crawler.

Client side rendering, or CSR. Your server sends a near-empty HTML shell plus JavaScript. The browser runs that JavaScript and builds the page. A crawler that does not run JavaScript receives the shell.

Static site generation, or SSG. Pages get built once, at deploy time, and served as finished files. For a marketing site this is usually the cheapest correct answer.

Hydration is the step where JavaScript takes over an already-rendered page to make it interactive. Content that exists only after hydration is content a crawler never sees.

What usually hides

The hero, most often, which is the worst possible thing to lose.

In order of how often I find them missing from the server response:

  1. Hero headline and subhead, when they come from a CMS field loaded by a client-side call.
  2. Tabbed content. Only the active tab renders, and sometimes not even that.
  3. Accordions and FAQ toggles, where answers load on click.
  4. Carousels. Slides two through six frequently never exist server side.
  5. Capability and feature lists pulled from a headless CMS at runtime.
  6. Customer logos, review widgets and pricing tables injected by third-party scripts.

A related failure shows up in the same crawl. 360 of 3,236 homepages had no H1 in the server HTML at all, and 235 of those did have an H2. The headings exist. The one that names the topic does not.

What it costs you

Your best sentence scores zero.

Rendering catches good teams. You write a claim nobody else could make. You get it approved. You ship it. No answer engine ever reads it. Defensible and unreadable produce the same silence as a claim any competitor could copy.

There is a cheap version of this failure too. A named framework rendered as three single words across a row, with no sentence around them and no company name attached. A human infers the meaning from the layout. A retrieval system reads three adjectives.

How do you fix it?

In cost order. Most sites never need step three.

  1. Move the claim into static HTML. Hard-code the hero headline, subhead and primary claim into the template instead of fetching them. This is an hour of work and it solves the majority of cases.
  2. Server-render or pre-render the pages that matter. Your homepage, product pages, pricing and top articles. Next.js, Nuxt, Astro and SvelteKit all do this without a rewrite. Leave the app behind your login as client side, because no crawler needs it.
  3. Change the rendering mode for the whole marketing site. Only worth it when the first two cannot cover your templates.

Two more checks while you are in there. Confirm every page has exactly one H1 in the server HTML. Confirm the <title>, meta description and Open Graph tags are server-rendered too. Those get read when the page is shared.

What this does not fix

Three things, and conflating them wastes a quarter.

Access. Rendering means nothing if the crawler never arrives. Check which bots your robots.txt and your firewall allow first. In my crawl, 34 sites blocked ChatGPT at the server, most of them while robots.txt invited it in.

The claim itself. A readable sentence that any competitor could publish gives an engine nothing to select on. Rendering makes you legible, not distinctive.

Third-party presence. Most answers are assembled partly from sites you do not own. Fixing your own HTML does not put you on them.

And skip the tactics that look adjacent to this work and do nothing. llms.txt will not substitute for server-rendered text, which is the point of the refused list.

Ask your developer this

One question, and the answer takes a minute to verify. Does our homepage send the headline and the primary claim in the server response, before any JavaScript runs?

If the answer is no, you have found the cheapest fix in AI search. If the answer is yes, stop worrying about rendering and go count what the engines actually say about you.

The audit checks rendering against the claim itself, so you learn whether your sentence is unreadable, unremarkable, or both. It pairs that with 150 observations across ChatGPT, Google AI Mode and Perplexity.

See what the audit covers.