JavaScript Rendering and AI Crawlers: Why Your Product Page Looks Empty

The failure mode nobody tests for

You can have perfect product copy, complete schema, and an open robots.txt, and still be invisible to AI search — because the crawler that fetched your page never ran the code that puts your content on it.

This is the difference between what a browser shows you and what an AI crawler receives. Your browser downloads HTML, then executes JavaScript that fetches data, mounts components, injects review widgets, and populates the DOM. An AI crawler that does not execute JavaScript stops after step one. It reads the raw HTML response and moves on.

The most-cited evidence on this is the Vercel and MERJ analysis of AI crawler behavior, first published in December 2024. Their conclusion was blunt: none of the major AI crawlers render JavaScript. That list includes OpenAI's GPTBot, OAI-SearchBot, and ChatGPT-User; Anthropic's ClaudeBot; and PerplexityBot. Notably, these bots do request JavaScript files — Vercel measured JS files at 11.50% of ChatGPT crawler requests and 23.84% of Claude's — they just treat them as text rather than executing them. Downloading a bundle is not the same as running it.

Two exceptions matter. Google's Gemini inherits Googlebot's rendering infrastructure, so it processes JavaScript. AppleBot also renders through a browser-based crawler. Everything else in the AI answer stack reads what your server sent.

Why this hits ecommerce harder than content sites

A blog post is usually text in HTML. A product page is an assembly of live data, and modern storefronts increasingly assemble it in the browser.

The pieces most likely to be client-rendered on a store are exactly the pieces AI engines need to recommend you:

  • Price and availability. Currency switchers, market-based pricing, and inventory lookups often resolve after load. The HTML ships a placeholder, the script fills in the number.
  • Reviews and ratings. Nearly every review app injects its widget — and frequently its AggregateRating JSON-LD — after mount, or renders it inside an iframe. A non-rendering crawler sees neither the stars nor the schema.
  • Variant data. Size, color, and per-SKU stock are commonly hydrated from a JSON blob by theme JavaScript rather than written into the markup.
  • Tabs and accordions. Specs, materials, sizing, care instructions, and shipping details tucked into lazy-loaded tab panels that only fetch on click.
  • Structured data injected by apps. Schema written by a client-side script is schema that many crawlers never see, no matter how correct it is.

Headless builds are the sharpest version of this problem. A React or Vue storefront without server-side rendering ships an app shell, and the shell is all the crawler gets. But this is not only a headless issue — plenty of conventional themes have quietly moved core product facts into client-side fetches, and app ecosystems make it worse with every widget installed.

How to check in under two minutes

Do not use your browser's inspector. The Elements panel shows the rendered DOM, which includes everything JavaScript did. You need the raw response.

1. View source, not inspect. Open the product page, use View Page Source (or view-source: before the URL), and search that text for your price, your product description, your review count, and your AggregateRating block. If they are not there, the crawler does not have them either.

2. Fetch it from the command line. curl -s https://yourstore.com/products/example | grep -i "priceCurrency" gives you the same answer without browser interference. Run it against your top ten revenue products, not just one.

3. Disable JavaScript and reload. In Chrome DevTools, use the command menu to disable JavaScript, then hard-reload the product page. What remains on screen is roughly what a non-rendering crawler works with. This is the version of your store that ChatGPT and Claude are reading.

4. Check your logs. Server logs tell you which AI bots are actually fetching, how often, and what status codes they get. Whether they can parse what you return is a separate question — but you need both halves. If you have not set up bot log monitoring yet, that pairs directly with measuring AI search visibility.

Fixes, in order of impact

Server-render the commercial facts. Product name, description, price, currency, availability, brand, GTIN or MPN, and variant-level data belong in the initial HTML response. Everything else — carousels, recommendation strips, chat widgets — can hydrate later without cost. The rule is simple: if a shopper needs it to decide, or an agent needs it to compare, it ships in the HTML.

Move JSON-LD out of JavaScript. Emit your Product schema from the template layer, not from an app script. On Shopify, that means rendering it in Liquid from product and metafield data rather than relying on an app to write it after load. The product schema markup guide covers the field-level requirements; the point here is only about when it appears.

Server-render ratings even if the widget stays client-side. You do not need to abandon your review app. Write the aggregate rating value, review count, and a handful of review excerpts into the server-rendered HTML — many apps expose this data through metafields or an API — and let the interactive widget lazy-load below the fold. You keep the page speed and gain the visibility.

Flatten hidden content into the markup. Tab panels and accordions are fine as long as the content is present in the HTML and merely hidden with CSS. What breaks is fetching that content on click. Change the pattern from fetch-on-demand to render-then-hide.

If you are headless, use SSR or static generation. Client-side rendering for product and collection pages is not a defensible choice for a store that wants AI visibility. Next.js, Nuxt, Hydrogen, and Astro all render server-side by default for a reason. The tradeoffs between rendering strategies are covered in more depth in Shopify Liquid vs headless.

Audit after every app install. The most common way a working product page becomes an empty one is a new app that replaces server-rendered markup with its own client-side block. Re-run the view-source check after theme updates and app installs, not once a year.

The underlying principle

AI crawlers optimize for cost. Rendering JavaScript at web scale is expensive, and for now most of them have decided the raw HTML is good enough. That decision is not yours to argue with — it is a constraint to design around.

Treat your server response as the canonical version of your store. If a fact is not in it, assume the engine does not know it. A technical foundation review usually starts here, because no amount of content strategy compensates for a page that arrives empty.

Want to see how AI engines perceive your brand?

Get Your Free AI Visibility Audit