Warningagents.js-only-content

The page's words arrive by JavaScript

Why it matters

Most AI crawlers read the HTML a server sends and nothing more. Measured across their traffic in December 2024, Vercel and MERJ found that "none of the major AI crawlers currently render JavaScript": ChatGPT's, Claude's and Perplexity's among them. Gemini, which uses Googlebot, and Applebot were the exceptions. A page that sends an empty shell and draws its words with JavaScript is, to those crawlers, a page with nothing on it, and an AI answer can't quote what it never saw.

Google renders JavaScript for Search ("Google is able to process content within JavaScript as long as it isn't blocked"), so this can be invisible in Search Console and still cost a site every other AI answer. The same is true of link previews: a chat app's preview reads the raw HTML too.

We call a page a shell when its HTML holds fewer than 50 words and shows an app drawing the rest: an empty #root, #app, #__next or <app-root>, or a <noscript> asking for JavaScript on a page with no headings. (Many themes put that <noscript> on every page, so on a short page that has a heading of its own it says nothing.) That is our rule of thumb, and the measurement is nearly two years old: crawlers change, which is why the source is watched.

Sources: Vercel: the rise of the AI crawler, Google's guide to generative AI features in Search.

How to fix it

Send the words in the HTML, so a reader that runs no JavaScript gets the page:

  • Render on the server or ahead of time. Next.js, Nuxt, SvelteKit, Astro and Angular all do it; a client-only React or Vue app can be prerendered for its public pages.
  • Keep the app for what is interactive, and let the text, headings and links arrive as HTML first.

Check what a crawler gets with curl -s https://your.site/page | less: the words should be there.

Examples

These are the rule's own test pages, and what it says about each. They are run on every change to the rule book.

<div id="root">
<script src="/static/js/main.4f2a.js">

WarningThe HTML holds 0 words and an empty <div id="root">. The rest is drawn by script, which most AI crawlers don't run, so to them the page is nearly empty.

fail-empty-root
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="shell">
<span>Loading</span>
<script src="/app.js">

WarningThe HTML holds 1 word and a <noscript> asking for JavaScript. The rest is drawn by script, which most AI crawlers don't run, so to them the page is nearly empty.

fail-noscript
<div id="root">
<main>
<h1>Acme dashboard</h1>
<p>Track every order, refund and shipment in one place, with alerts when anything goes wrong.</p>
</main>
<script src="/static/js/main.4f2a.js">

PassedThe page's words are in its HTML (17), where AI crawlers can read them.

pass-server-rendered