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>, thenog:titleor<title>. - Price visible: the price from JSON-LD or
product:price:amount/og:price:amountappears 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:
priceCurrencyin JSON-LD or aproduct:price:currency/og:price:currencytag. - Availability known:
offers.availabilityin 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
- Fetch product data on the server. Move the product request from a client-side
useEffector API call into server-side rendering, static generation or incremental regeneration. - Render the price as text with its currency.
$49.00or49,00 €in the markup, not just a number inside a JSON blob. - Render an explicit stock string. “In stock” or “Out of stock”, not only a disabled button or a color.
- Back it with structured data. Add Product JSON-LD with
priceCurrencyandavailabilitygenerated 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
priceCurrencyandavailability, 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-keyheader (publishable API keys). - Next.js (any backend): fetch product data in a server component, or in
getServerSideProps/getStaticPropson 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
- Google: JavaScript SEO basics
- Vercel: The rise of the AI crawler (log study, secondary source)
- Google merchant listing structured data
- schema.org ItemAvailability
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