Skip to content
AEO Canon · the reference for answer-engine optimization

Why JavaScript Breaks Your AI Citation Eligibility

Vercel and MERJ analyzed 500M+ GPTBot requests and found zero JavaScript execution — so content rendered client-side is invisible to AI crawlers. If your answer only appears after JS runs, you can't be cited. Here are the SSR, SSG, and prerender fixes, with code.

BBurke Atkerson4 min read

JavaScript breaks AI citation eligibility because most AI crawlers don't run it — Vercel and MERJ analyzed over 500 million GPTBot requests and found zero JavaScript execution. If your content is assembled client-side, it isn't in the raw HTML the crawler reads, so you're invisible no matter how good the content is. The fix is to render on the server.

The short answer

AI crawlers read raw HTML and don't execute JavaScript (Vercel/MERJ, 500M+ GPTBot requests). Client-rendered content isn't in that HTML, so it can't be cited. Move citable content to SSR, SSG, or prerender so it's in the initial response.

What the AI crawler receives

Client-rendered

An empty shell — <div id="root"></div> and a script bundle — with no content, because the crawler never runs the JavaScript that would build the page.

Server-rendered

The full HTML in the first response: headings, the answer passage, links — everything present and citable, no JavaScript required.

Why it works · Most AI crawlers read raw HTML and do not execute JavaScript — Vercel/MERJ measured zero JS execution across 500M+ GPTBot requests.

Why can't AI crawlers see client-rendered content?

AI crawlers can't see content built by client-side rendering because they fetch the raw HTML and don't run the JavaScript that would build it. The Vercel/MERJ analysis of 500M+ GPTBot requests found no JavaScript execution at all — the crawler takes the server's initial response and nothing more. A client-rendered app typically ships a near-empty shell like this:

<!-- What the server sends for a client-rendered app -->
<!doctype html>
<html>
  <head><title>My App</title></head>
  <body>
    <div id="root"></div>
    <script src="/static/bundle.js"></script>
  </body>
</html>

A browser runs bundle.js, which fetches data and fills #root with your content. A crawler doesn't — so it sees an empty #root and concludes the page has no content. This is the access gate failing at the rendering layer: permission was fine, but there's nothing to read.

How do you confirm this is your problem?

Confirm it by comparing the raw HTML to the rendered page: if your answer is on screen but missing from curl, JavaScript is the culprit.

# If this prints nothing but the text is clearly on the page, it's client-rendered
curl -s -A "GPTBot" https://yourdomain.com/ | grep -i "your answer phrase"

The full procedure — including the JS-disabled browser test — is in how to check if AI crawlers can read your site.

What are the fixes?

The fix is to get your content into the initial HTML response through server-side rendering, static generation, or prerendering. All three produce HTML that already contains your answer before any JavaScript runs.

Rendering strategies that make content crawler-visible
StrategyWhen it rendersBest for
Static generation (SSG)At build timeContent that changes infrequently — docs, articles, marketing
Server-side rendering (SSR)At request time, on the serverPersonalized or frequently-changing content
PrerenderingCached HTML served to botsExisting SPAs you can't fully rewrite
Client-side only (CSR)In the browser, after fetchLogged-in app views that needn't be cited

Static generation or server-side rendering (Next.js)

In an App-Router framework, components render on the server by default, so the answer is in the HTML. A Server Component that fetches data renders fully before it reaches the browser — or the crawler:

// app/guides/[slug]/page.tsx — renders on the server; HTML ships with content
export default async function GuidePage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const guide = await getGuide(slug); // runs on the server
 
  return (
    <article>
      <h1>{guide.title}</h1>
      {/* This answer is in the initial HTML — crawlers can read it */}
      <p>{guide.answer}</p>
    </article>
  );
}

For static generation, pre-build the pages so they're served as ready HTML:

// Pre-render every guide at build time (SSG)
export async function generateStaticParams() {
  const guides = await getAllGuides();
  return guides.map((g) => ({ slug: g.slug }));
}

Prerendering for an existing SPA

If you can't rewrite a single-page app, add a prerender layer that serves cached, fully-rendered HTML to crawlers. The deeper patterns are in how to make a single-page app citable by AI.

Can you still use a JavaScript framework?

Yes — the problem was never JavaScript frameworks; it's shipping an empty shell that only fills in client-side. React, Vue, Svelte, and their meta-frameworks (Next.js, Nuxt, SvelteKit, Remix) all support server rendering. Render citable content on the server, then hydrate for interactivity: crawlers get full HTML, users get the app.

The 'works in my browser' trap

Client-rendered content looks perfect in your browser, in screenshots, and in Inspect Element — so the problem hides until you check the raw HTML. Don't trust the rendered page; trust curl and View Source. The content that isn't in the initial HTML doesn't exist as far as the crawler is concerned.

Render for crawlers

0 / 5

Each unchecked box is a place a competitor can beat you to the AI answer.

Where this fits in the Canon

This is the rendering half of the access pillar — permission (robots.txt) gets the crawler in, but rendering decides whether there's anything for it to read. Once content is in the HTML, extractability decides how liftable it is.

Related: server-side vs client-side rendering for AI crawlers, how to make a single-page app citable, and how to check if AI crawlers can read your site.

Frequently asked questions

Why does JavaScript break AI citations?
Because most AI crawlers don't execute JavaScript. Vercel and MERJ, analyzing over 500 million GPTBot requests, found zero evidence of JavaScript execution — the crawler reads the raw HTML and stops. If your content is rendered client-side, it isn't in that raw HTML, so the crawler sees an empty shell and can't cite what isn't there.
Do any AI crawlers run JavaScript?
Google's Gemini is the main exception, because it can use Google's existing rendering infrastructure. For OpenAI's GPTBot and OAI-SearchBot, Anthropic's crawlers, and PerplexityBot, you should assume no JavaScript execution. Designing for the non-rendering case keeps you citable everywhere rather than only on the one engine that renders.
How do I fix client-rendered content for AEO?
Render the content on the server so it's in the initial HTML response. Use server-side rendering (SSR) or static site generation (SSG) in a framework like Next.js, Nuxt, or Remix, or add prerendering for a pure single-page app. The goal is that a curl fetch — with no JavaScript — already contains your answer text.
Is client-side rendering always bad for AEO?
Not always — it's fine for content that doesn't need to be cited or indexed, like logged-in dashboards. It's only a problem for public, citable content. The fix isn't to abandon JavaScript frameworks; it's to render the citable content on the server and hydrate for interactivity, so crawlers get full HTML and users still get an app.

Last updated .

Part of

Related reading

Auto detailers should use AutomotiveBusiness (a LocalBusiness subtype) schema with accurate name, address, phone, service area, hours, and services, plus FAQ schema on answer pages — it helps engines parse who you are. Schema clarifies content for AI; it never rescues a thin site or a buried answer.

2 min read

A detailing business needs a website rebuild for AEO when it lives on social media with no real site, is slow, or lacks per-package answer-first pages and schema — because the engine can only recommend what it can read. The rebuild is the access layer everything else depends on.

2 min read

Auto repair shops should use the AutoRepair (a LocalBusiness subtype) schema with accurate name, address, phone, service area, hours, and services, plus FAQ schema on answer pages — it helps engines parse and confirm who you are. Schema clarifies content for AI; it never rescues a slow site or a buried answer.

2 min read