Content that only exists after JavaScript runs is not on your page as far as most AI crawlers are concerned. The crawler requests the URL, receives the HTML your server sent, and works from that. If the body copy, the review text, the second tab of the spec sheet, or the next twenty products arrive later by script, they were never in the document that crawler holds.
Google is the documented exception, and even Google does not click. So the useful question for any page is not “does it use JavaScript” but “which of its words are in the initial response, and which arrive only after a script runs or a person acts.” This post covers who renders, the page patterns that lose content, how to compare view-source against the rendered DOM by hand and across a site, and what to change.
It sits under Answer Engine Optimization: a page-level guide and is the detailed version of one row in what do AI crawlers extract from your page?, which covers everything else that survives extraction.
Which AI crawlers render JavaScript?
Google documents that it renders; Apple documents that Applebot may; OpenAI, Anthropic, and Perplexity document nothing about rendering either way. Independent measurement from late 2024 found that their crawlers did not execute JavaScript.
| Crawler | What the vendor documents about rendering | Evidence beyond the docs |
|---|---|---|
Googlebot (feeds AI Overviews and AI Mode) | Renders: Google processes JavaScript in “three main phases,” crawling, rendering, and indexing (Google, last updated 2026-03-04) | — |
Applebot | ”Applebot may render the content of your website within a browser” (Apple) | — |
OAI-SearchBot, GPTBot, ChatGPT-User | Nothing (OpenAI) | Measured not executing JavaScript |
Claude-SearchBot, ClaudeBot, Claude-User | Nothing (Anthropic) | Measured not executing JavaScript (ClaudeBot) |
PerplexityBot, Perplexity-User | Nothing (Perplexity) | Measured not executing JavaScript (PerplexityBot) |
The measurement is a joint analysis by Vercel and MERJ, which concluded that “none of the major AI crawlers currently render JavaScript,” naming OpenAI’s three agents, ClaudeBot, PerplexityBot, and others, and which advised site owners to “prioritize server-side rendering for critical content” (Vercel, 17 December 2024). It is measured rather than declared, and it is nearly two years old. Treat it as the best available evidence and confirm against your own logs, not as a permanent property of any crawler.
Google states the general version of the same point on its own JavaScript page: “not all bots can run JavaScript,” which is part of why it recommends server-side or pre-rendering.
The practical consequence is an asymmetry. A client-rendered page can be indexed by Google and appear on its AI surfaces while being empty to every other assistant’s crawler. A page that shows up in AI Overviews is not evidence that it is readable anywhere else.
Even Google doesn’t click
Rendering executes the scripts that run on load. It does not press buttons. Google’s lazy-loading guidance is explicit: “Google Search does not interact with your page,” so content must load when it is visible in the viewport, not in response to scrolling or clicking (Google Search Central, last updated 2025-12-10).
That splits interactive content into two kinds, and the split is the most useful idea on this page:
- Hidden but present. The text ships in the HTML and CSS hides it until someone opens the tab or accordion. Every crawler that reads the response has it.
- Absent until asked for. The text is fetched from the server when someone clicks, scrolls, or presses “load more.” No crawler that does not interact has it, and per the documentation above, that includes Google.
From the outside the two look identical. In view-source they are not.
What never makes the trip
These are the patterns that most often take content out of the response. Each row says what a non-rendering crawler receives and what a renderer that does not interact receives.
| Pattern | Non-rendering crawler | Rendering, no interaction |
|---|---|---|
Client-rendered app shell: body is an empty root div plus scripts | Nothing but the shell, title, and whatever is in the head | The rendered body |
| Tabs or accordions whose panels are fetched on click | Only the visible panel, if that | Only the visible panel |
| Tabs or accordions with all panels in the HTML, hidden by CSS | Everything | Everything |
| ”Load more” button or infinite scroll fetching the next items | The first batch | The first batch |
| Reviews, ratings, or Q&A injected by a third-party script | Nothing | Usually the rendered widget |
| JSON-LD inserted by a tag manager after load | No structured data | The structured data |
| Navigation built from elements with click handlers instead of links | No links to follow | No links to follow |
| Text drawn on a canvas or baked into an image | Nothing | Nothing |
The navigation row matters more than it looks. Google states it “can only discover your links if they are <a> HTML elements with an href attribute.” A site whose category menu is a set of click handlers has pages no crawler can reach by following links, whatever it can render.
The accordion rows are the ones FAQ sections run into; FAQ sections: leverage or filler covers whether those blocks earn their place, and this table covers whether their answers are in the response at all.
How to check one page by hand
You need a browser and a sentence from the page that you want quoted.
1. View source. Put view-source: in front of the URL and use the find bar for that sentence. View-source is the response before any script ran, which is what a non-rendering crawler receives. If the sentence is not there, stop: the rest of the page’s AEO work does not reach those crawlers.
2. Compare against the rendered DOM. Open developer tools and look at the Elements panel. This is the page after scripts ran. Anything you can find in Elements but not in view-source was added by JavaScript. Check headings, the first paragraph under each, tables, review text, and prices, since these are what an assistant would lift.
3. Turn JavaScript off. In developer tools, open the command menu, run “Disable JavaScript,” and reload. What remains on screen is a fair picture of what a non-rendering crawler can read. Empty sections, spinners, and “loading” placeholders are findings.
4. Click every tab and accordion with the Network panel open. Filter to fetch and XHR requests, then open each panel. If opening a panel fires a request, its content was not in the response. If nothing fires, the content was already in the HTML.
5. Scroll to the bottom of any list. If new items arrive as you scroll, note how many were present in view-source. That is how many any crawler that does not interact will ever see from this URL.
Five checks, about ten minutes, and each produces a yes or no you can write in a ticket.
How to check it across a site
The by-hand method scales through a crawler that can run with rendering on and off.
In a crawler, without a terminal. Crawl the same URL list twice: once with rendering disabled and once with rendering enabled. Add custom-extraction rules that count what you care about, such as string-length(normalize-space(//main)) for main-content length, count(//h2) for headings, and a count of your review or product-card selector. Export both runs to a spreadsheet, match them on URL, and add a column for the difference. Sort by it. URLs where rendering adds most of the text are the client-rendered ones; URLs where it adds the review count but nothing else have a widget problem, not a template problem.
Also compare the page counts. A rendered crawl that discovers many more URLs than a static one means some navigation depends on script, and the extra URLs are pages a non-rendering crawler cannot reach by following links.
In GEOBee, the quick scan is the non-rendering view: it fetches each page’s HTML and scores it without a browser, and the Words column reports main-content word count per URL. The deep scan runs each page through Lighthouse and adds JavaScript-discovered links, URLs that only appear after scripts run (scan modes). A template whose pages score near-empty in a quick scan, or a section that only appears in a deep scan, is the finding.
From a terminal. To check that one sentence is in the raw response for one URL:
curl -s "https://example.com/your-page/" | grep -c "the sentence you want quoted"
A count of 0 on a page where the sentence is visible in the browser means it arrives by script. Run it with a bot user-agent if you also suspect the edge is serving bots something different; how to check whether AI crawlers can actually reach you covers that comparison.
What to change
The fixes are about where content lives, not about removing JavaScript.
Server-render or pre-render the content that answers questions. Body copy, headings, specification tables, prices, and the first answer under each heading should be in the HTML response. Interactivity can be added on top. This is the change both Google’s guidance and the published measurement point to.
Ship every tab and accordion panel in the HTML. Hide panels with CSS, or use the native <details> element, rather than fetching them on click. The page looks the same to a reader and is complete for every crawler.
Give paginated and infinite lists real URLs. Google’s guidance for infinite scroll is to “give each chunk its own persistent, unique URL” and link them in sequence. A “load more” button can stay, as long as page two exists at its own address with a normal link pointing to it.
Put structured data in the initial HTML. JSON-LD inserted by a tag manager is invisible to crawlers that do not render, and it can drift from the page because nobody sees it in the template.
Use links for navigation. Every route a reader can take should be an <a href> in the response.
Keep the template from swallowing what is left. Once the content is server-rendered, the next question is whether an extractor picks the right block; boilerplate ratio is the measurement for that.
Where this fits in an audit
Rendering is layer 2 of the audit loop: the check that a successful fetch actually delivered the page. It comes after access and before any answer-readiness work, because a perfect answer block that arrives by script is not in the response a non-rendering crawler reads. How to audit a site for AEO and GEO puts it in sequence with the other four layers.
What a server-rendered page buys you is narrow and verifiable: the same words reach every crawler that is allowed to fetch it. Whether any of them use those words is a separate question, and not one rendering answers.
To see which templates arrive near-empty without a browser, and which URLs only appear once scripts run, download GEOBee and compare a quick scan with a deep scan of the same site during the 7-day trial.