Blog · · 3 min read

Is your price in the HTML? Server rendering for AI agents

ChatGPT's and Claude's crawlers read your HTML without running JavaScript. If your prices and answers load in the browser, they see an empty page. Here is how to test it and fix it.

ChatGPT's and Claude's crawlers download your page and read the HTML without running its JavaScript. If your prices, product details or answers only appear after scripts run, those crawlers find an empty page.

Vercel measured this across its network: the crawlers behind ChatGPT and Claude fetched JavaScript files but did not execute them, while Google's Gemini and Apple's crawler did render pages in a browser (Vercel). You can't choose which crawler visits, so the safe default is to put the answer in the HTML the server sends.

Test it in a minute

Ask your server for a page and search the response for something a customer would look for:

curl -s https://yourcompany.com/pricing | grep -i -o ".\{40\}\$49.\{40\}"

If your price shows up, it's in the HTML. If the command prints nothing, the price is added later by JavaScript, and many agents will never see it.

You can also turn JavaScript off in your browser's developer tools and reload. What's left on screen is roughly what a crawler reads.

What "readable" means

Dedica's check fetches each page with JavaScript off and looks at what's left: whether there is a main heading, whether the content is there, and how much of the HTML is text a reader can use.

The worst case is an empty shell, where the HTML is a single <div id="root"></div> and a script tag and everything appears later. That fails the check. A milder case is a page whose text is present but buried in markup and scripts. When we scanned stripe.com in October 2026, its homepage had a proper heading and over 10,000 characters of text, but that text was only 2.8% of the HTML, below the 5% we look for, so it passed with a warning.

Fixing it, by stack

Next.js and other React frameworks

In the Next.js App Router, components render on the server by default. The usual mistake is fetching the important content inside useEffect or another client-side hook, which moves it out of the HTML.

Move the data fetch into a server component or the route's page.tsx, and pass the result down:

// app/pricing/page.tsx
export default async function PricingPage() {
  const plans = await getPlans(); // runs on the server
  return <PlanTable plans={plans} />;
}

We made this change on Dedica's own report pages. Before, the score and findings loaded in the browser and the server sent 41 words of HTML. After moving the fetch to the server, the same page sends about 2,500 words, including the score, the grade and every finding.

Single-page apps (Vite, Create React App)

A client-only app sends an empty shell for every route. Prerender the pages that carry answers, such as home, pricing, product and docs, at build time, or move those routes to a framework that renders on the server. The app can stay interactive after it loads, because only the first HTML needs the content.

Shopify, WordPress, Webflow

These platforms render pages on the server, so the theme's own content is usually fine. The trouble comes from add-ons that inject content with scripts, such as price widgets, review apps and pricing calculators. Run the curl test on the pages where those appear, and prefer apps that render on the server or offer a static fallback.

Keep facts as visible text

Structured data such as JSON-LD helps, but many agents read a page's text and skip <script> blocks. If your annual price only exists inside a JSON blob that a toggle reads, write it in the page as text too.

Check the result

Run your site through Dedica after the change. The "Content without JavaScript" check in the report shows whether your key pages now carry their content in the HTML.

Sources

Check your site

See what an agent finds on your site, and which of these problems you have.