Dynamic rendering was the workaround Google told serious teams to grow out of. Detect the crawler, run the JavaScript somewhere else, serve the bot a static HTML snapshot. It solved a real problem for a while. Then Googlebot got good enough at JavaScript that the workaround became technical debt.
The wrong conclusion is that the problem disappeared. It did not. It moved. Googlebot can usually render the modern web. Many AI bots still cannot. They fetch the first HTML response, strip what they can, and leave. If your category page is a React shell until JavaScript wakes it up, the model sees the shell.
The right answer is not to bring old dynamic rendering back unchanged. The right answer is a simpler pattern built for AI retrieval: Lean Render. Not browser parity. Not a screenshot of your app. A clean, truthful, token-efficient HTML path for bots that need facts, links, headings, and schema.
Why it died for Google
Google's own documentation now calls dynamic rendering a workaround, not a long-term solution, and recommends server-side rendering, static rendering, or hydration instead. The old pattern existed because search crawlers had trouble with JavaScript-generated content. Once Googlebot could render most pages well enough, maintaining a separate crawler path became expensive, fragile, and easy to get wrong. See Google's dynamic rendering guidance.
Rendertron is the tombstone. It was Google's open-source headless Chrome rendering solution. The repository was archived in 2022 and now says dynamic rendering is not recommended because better rendering approaches exist. That is the Google-era story: stop bolting a browser to the side of the stack; render the page properly in the application.
Why it is back for AI
AI crawlers reset the clock. OpenAI documents separate crawlers for search and model
training, including OAI-SearchBot and GPTBot. Anthropic
documents ClaudeBot, Claude-User, and
Claude-SearchBot. Perplexity documents PerplexityBot and
Perplexity-User. Google has Google-Extended as a robots
control token for Gemini training and grounding. These are not all Googlebot. They do
not all behave like Googlebot.
In practice, many AI fetchers are closer to raw HTTP clients than full browsers. They want the URL, the text, the headings, the links, and the structured data. They do not want a megabyte of JavaScript, a consent manager, a carousel framework, an analytics bundle, and a hydration race. This is why JavaScript-heavy sites can rank in Google and still fail Fetch mode inside an AI answer.
A cautionary tale
We audited a production prerender pipeline recently. The architecture looked normal: edge routing detected crawler user-agents, sent them to a prerender backend, launched Puppeteer, waited for the React app to render, captured the final DOM, cached the HTML, and served it to bots. On paper, that is classic dynamic rendering.
The failure was not in the idea. It was in the state. The edge layer stripped cookies before requests reached the prerender backend. The React app needed some of that state to complete its API calls during the render. Without it, the app rendered a system error. Puppeteer did exactly what it was told to do: it captured the page. The cache did exactly what it was told to do: it stored the HTML. Bots then received a perfectly cached error page.
That is the lesson. Separating fetch as bot from fetch as user is necessary, but it creates a new class of bugs: wrong cookies, wrong headers, wrong wait conditions, wrong cache keys, wrong fallback behavior. A browser snapshot can be worse than an empty shell because it looks successful at the HTTP layer. Status 200. HTML returned. Cache hit. Content wrong.
Why old snapshots fail
The failure mode is structural. A browser snapshot inherits every dependency of the human app: cookies, feature flags, API timing, third-party scripts, consent state, personalization rules, CDN cache keys, and whatever error handling the frontend team wrote for real users. That is too much machinery for a bot representation whose job is to state facts.
The most dangerous failures are the quiet ones. A timeout usually logs an error. A blocked request often returns a 403. But a React error screen captured as HTML returns 200, looks cacheable, and can sit at the edge for hours. The monitoring layer sees bytes. The crawler sees text. The model sees a brand that apparently has no useful product information.
Lean Render starts from the opposite premise: the bot page should be generated from the source facts, not from the interactive app after it has survived a browser session. If the product data, canonical URL, price, availability, headings, links, and schema are known server-side, write them directly into the bot representation. Keep the interactive app out of the critical path.
Introducing Lean Render
Lean Render is a bot-serving pattern for AI retrieval. When a known AI crawler or AI fetcher requests a page, the edge serves a small, static, semantic HTML representation of the same facts a human page contains. The goal is not to make a bot experience your application. The goal is to make the facts readable, citable, and cheap to ingest.
This is not dynamic rendering reborn. Dynamic rendering tried to create JavaScript parity for search crawlers. Lean Render creates retrieval parity for AI systems. The human path can remain a rich product experience. The bot path should be the cleanest possible evidence packet.
What Lean Render looks like
Start with explicit user-agent policy. The allowlist should include the AI surfaces
you want to serve: GPTBot, OAI-SearchBot,
ChatGPT-User where appropriate, ClaudeBot,
Claude-User, Claude-SearchBot, PerplexityBot,
Perplexity-User, Google-Extended, and any client-approved
vertical crawlers. Match by verified IP ranges where the vendor publishes them. Do
not trust user-agent alone for security decisions.
Then serve clean static HTML from the edge cache. Not a live Puppeteer snapshot of your React, Vue, or Svelte app. Not a 2.5 MB DOM dump. The bot path should be an application-owned representation of the page: title, canonical, entity facts, product facts, comparison facts, availability, links, and schema.
Strip navigation, footers, cookie banners, ads, carousels, tracking pixels, empty
app containers, and layout-only wrappers. Inline JSON-LD near the top of the body.
Use semantic HTML: <main>, <article>, one
<h1>, ordered heading hierarchy, useful anchors, compact tables,
and plain text. The target is less than 3,000 tokens for most pages. A human path can
be 30,000 tokens of scripts, chrome, and interaction. The bot path should not be.
Cache it like a separate representation. Use Vary: User-Agent or a
stronger internal bot-bucket key. Set Cache-Control separately from the
human path. Purge it when the source facts change. Log it as its own channel, because
if Lean Render breaks, AI Fetch breaks even while the human page looks fine.
What Lean Render is not
| Anti-pattern | Why it fails |
|---|---|
| Puppeteer rendering a JS app on demand | Fragile state, timeout, cookie, and cache failures; expensive at scale. |
| Cloaking | Lean Render serves the same facts, only stripped of UI clutter and scripts. |
| Different commercial claims for bots | That is a trust failure. Presentation may differ; truth must not. |
| Only an SEO-bot feature | The primary customer is now the AI fetcher: ChatGPT, Claude, Perplexity, Gemini grounding. |
| A replacement for SSR | If you can ship good SSR or static generation for everyone, do that. Lean Render is the controlled bot path when you cannot. |
The fetchability checklist for 2026
- SSR or Lean Render. The important facts must exist before client-side JavaScript runs.
- TTFB under 800ms. AI fetchers have less patience than humans and fewer retries than search engines.
- Clean HTML. One
<main>, one<h1>, useful headings, and at least five real anchors on indexable pages. - Robots allowlist. Explicitly decide access for OpenAI, Anthropic, Perplexity, Google, Meta, Common Crawl, and vertical bots.
/llms.txtand/llms-full.txt. Treat them as navigation aids, not magic ranking files.- JSON-LD validated. Put Organization, Product, Breadcrumb, FAQ, Review, or Article facts where relevant.
- No cookie wall blocking text. Consent UI can sit on top of content; it should not be the content.
- No PDF-only facts. If a model needs to know it, put it in HTML text as well.
- Accurate status codes. No soft 404s served as 200. No error state cached as success.
- Correct variant headers. Bot and human paths need separate cache keys and observable headers.
How Combot tests this
Combot's Monitoring URL DB treats this as an operational surface, not a theory. Every
priority URL gets a fetcher matrix: claude_bot_sim,
gptbot_sim, googlebot_mobile, and combotbot.
The drawer shows status, TTFB, bytes, word count, anchor count, schema, robots access,
and parseability for each fetcher. It also compares raw HTML against rendered DOM so
the team can see exactly what disappears when JavaScript is not executed.
That matrix is the artifact. If Googlebot Mobile sees 900 words and 60 links, but
gptbot_sim sees 12 words and zero links, the problem is not content
strategy. It is Fetch mode. If claude_bot_sim gets a cached error page
while the human browser gets a working app, the problem is not ranking. It is the
bot path. Lean Render gives that path a simpler contract: same facts, less machinery.
Dynamic rendering tried to help search engines behave like browsers. Lean Render helps AI systems behave like readers.
Further reading: Knowledge Modes · 7 layers · Technical SEO of LLMs
