Is your storefront agentic?

Server-render product name, price and stock for AI agents

AI shopping agents and AI search bots such as ChatGPT, Claude and Perplexity read the HTML your server returns and do not run JavaScript. An agent-ready product page has the product name, the price with its currency, and the stock status in that initial HTML, not loaded later by a client-side script.

Updated · By the Medusa team

Category
Product data (25% of score)
Impact
High
Scanner check
Product name, price and availability are in the server HTML

Why AI agents need product facts in the server HTML

An AI agent comparing products needs four facts before it can recommend or buy: what the product is, what it costs, in which currency, and whether it’s in stock. If those only appear after client-side JavaScript runs, most agents never see them.

A Vercel and MERJ analysis of AI crawler traffic found that GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot and PerplexityBot fetched JavaScript files but did not execute them; Googlebot and Applebot did render. Agents that use a “web fetch” tool behave the same way: they read the HTTP response and stop. Google’s own JavaScript SEO guidance recommends server-side rendering or pre-rendering for the same reason.

A client-rendered product page gives these agents an empty <div id="root"></div>, a spinner, or a price placeholder. The agent either skips the product or guesses the price from somewhere else.

What the scanner checks

The scanner fetches up to three sampled product pages without running JavaScript and extracts the visible text (scripts and styles removed). Each page is graded, and the worst page sets the result.

  • Name visible: at least 60% of the words (3+ characters) in the product name appear in the text. The name comes from Product JSON-LD, then the <h1>, then og:title or <title>.
  • Price visible: the price from JSON-LD or product:price:amount / og:price:amount appears in the text in a common format (1,299.50, 1.299,50, 1299.50). With no structured price, a currency symbol or code next to an amount ($49.00, 49,00 €) counts.
  • Currency stated: priceCurrency in JSON-LD or a product:price:currency / og:price:currency tag.
  • Availability known: offers.availability in JSON-LD, or text such as “In stock”, “Sold out”, “Pre-order” or “Ships in”.
Result Condition
Pass Every sampled page shows name and price in the text, with currency and availability known.
Partial Name and price are visible but currency or availability is missing; or the page is a JS shell whose data is only in a hydration blob (__NEXT_DATA__, self.__next_f.push, window.__NUXT__, __INITIAL_STATE__, __APOLLO_STATE__); or the name or price is missing but structured data exists.
Fail An empty shell (under 200 characters of text, or an empty #root / #__next / #app / <app-root>) with no structured data or hydration data, or neither name nor structured data present.
Inconclusive No product page could be fetched.

How to fix it

  1. Fetch product data on the server. Move the product request from a client-side useEffect or API call into server-side rendering, static generation or incremental regeneration.
  2. Render the price as text with its currency. $49.00 or 49,00 € in the markup, not just a number inside a JSON blob.
  3. Render an explicit stock string. “In stock” or “Out of stock”, not only a disabled button or a color.
  4. Back it with structured data. Add Product JSON-LD with priceCurrency and availability generated from the same data.

A minimal server-rendered product block:

<main>
  <h1>Merino Crew Sweater</h1>
  <p class="price">$89.00 USD</p>
  <p class="stock">In stock</p>
  <form action="/cart/add" method="post">
    <input type="hidden" name="id" value="4421" />
    <button type="submit">Add to cart</button>
  </form>
</main>

For a Next.js App Router page, fetch in the server component instead of a client component:

// app/products/[handle]/page.tsx (server component)
export default async function ProductPage({ params }: { params: Promise<{ handle: string }> }) {
  const { handle } = await params;
  const product = await getProduct(handle);
  return (
    <main>
      <h1>{product.title}</h1>
      <p>{formatPrice(product.price, product.currency)}</p>
      <p>{product.inStock ? 'In stock' : 'Out of stock'}</p>
    </main>
  );
}

Verify with curl -s https://your-store.com/products/example | grep -iE "merino|89\.00|in stock".

How to fix it on Medusa

  • What the starter does: the DTC starter storefront fetches the product from the Store API in the product page server component, so the name and price are already in the server HTML.
  • What to add: availability and currency are not stated explicitly. Add Product JSON-LD with priceCurrency and availability, or render a stock string next to the price.
  • Custom storefronts: fetch the product on the server, not in a client component. Every Store API request needs the x-publishable-api-key header (publishable API keys).
  • Next.js (any backend): fetch product data in a server component, or in getServerSideProps / getStaticProps on the Pages Router.

Common mistakes

  • Rendering the price only after a client-side currency or region lookup.
  • Hiding the real price behind a “Select a size” state until a variant is chosen. Render the default variant’s price.
  • Showing stock only as a disabled “Add to cart” button with no text.
  • Assuming Google’s rendering covers you. Googlebot renders JavaScript, but most AI bots and agents don’t.

Sources

Frequently asked questions

Do AI crawlers render JavaScript?
Most don't. A Vercel and MERJ log study found that GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot and PerplexityBot did not execute JavaScript, while Googlebot and Applebot did. Fetch-based agent tools also read the raw response.
My product data is in __NEXT_DATA__. Is that enough?
It counts as partial. The data is in the response, but only as a JSON blob an agent has to find and interpret. Render the name, price and availability as visible HTML text too.
How do I check what an AI agent sees on my product page?
Run curl on a product URL and search the output for the product name, the formatted price and a stock string such as "In stock". If they only appear after JavaScript runs, agents won't see them.

Run your store on a platform built for agentic commerce

Medusa is open-source, headless commerce with a full Store API agents can call and storefronts you control end to end.

Deploy on Medusa Cloud

Open-source commerce with a full Store API, hosted and scaled for you. Your storefront, your data model.

Storefront skills for coding agents

Teach Claude Code, Cursor or Codex to build storefronts with the patterns these checks look for.

npx skills add medusajs/medusa-agent-skills
View the skill on GitHub