JavaScript rendering: what Google sees, what the AI crawlers do not

Google can run your JavaScript, but it does it in a second pass, later and not always. The crawlers behind ChatGPT, Claude and Perplexity do not run it at all. If the words on your page only exist after a script finishes, you are invisible to some readers and late to the rest.

By , founder of Porteur · Updated 15 September 2026 · Markdown

The two waves, and why the second one is late

Googlebot fetches your HTML first. It reads what is there, follows the links it finds, and indexes what it can. Anything that only appears after JavaScript runs waits for a second pass, where the page is rendered with an evergreen version of Chromium and read again.

That second pass happens. It is just not on your schedule. A page whose content arrives with the HTML can be indexed the same day; a page whose content arrives after a script may wait, and when rendering fails it is never indexed at all.

  • First wave: the raw HTML your server sent. Fast, reliable, read by every crawler on the web.
  • Second wave: the rendered page after scripts run. Google only, queued, and dependent on your scripts loading correctly from a data centre.
  • No wave: the answering AI crawlers. They read the HTML and stop.

What breaks when the page is built in the browser

Client-side rendering does not fail loudly. The page looks right to you, because your browser ran the scripts. The failures are quiet and they all look the same in Search Console: a page that is crawled and not indexed.

  • Links that are not anchors. A div with an onClick handler is a button to a crawler, not a route to another page.
  • Content behind an interaction. Text that appears after a click, a scroll handler or a tab change is not seen; content in a collapsed panel that exists in the DOM is fine.
  • Routes behind a hash. Anything after the # is not sent to your server and is not treated as a separate page.
  • Data fetched on the client. If the product list arrives from an API after load, the first wave sees an empty shell.
  • Blocked resources. A robots.txt that disallows your script or style folder stops the render from completing.
  • Slow or failing scripts. The renderer gives up; you get the empty shell indexed, or nothing.
<!-- Not a link to a crawler -->
<div class="card" onclick="go('/guides/getting-started')">Getting started</div>

<!-- A link -->
<a href="/guides/getting-started">Getting started</a>

How to test what Google actually sees

Do not guess from your own browser. Two checks take five minutes and settle the question for one page.

  1. Look at the HTML your server sends

    View source, not the element inspector. The inspector shows the rendered DOM; view-source shows the first wave. Search it for a sentence from the middle of your page. If it is missing, that sentence needs JavaScript to exist.

  2. Inspect the URL in Search Console

    Open URL Inspection, run Test live URL, then View tested page. You get the rendered HTML Google produced, a screenshot, and a list of page resources that could not be loaded. Read the resource list first: a blocked script explains most failures.

  3. Check the console messages

    The same panel shows JavaScript errors from the render. An error in a data fetch usually means Google saw the empty shell.

  4. Compare the two

    If the rendered version holds the content and the source does not, the page depends on rendering. It will be indexed eventually, and it will be invisible to the AI crawlers until you change it.

The four rendering choices, and what each costs

Every framework offers some version of these. The question is not which is fashionable, but where the HTML is assembled.

ApproachWho assembles the HTMLWhat a crawler gets on the first fetchCost to you
Static generationYour buildThe whole page, instantlyA build step, and a rebuild when content changes
Server renderingYour server, per requestThe whole page, at the speed of your serverServer time, and caching to keep the first byte fast
Client renderingThe visitor's browserAn empty shellInvisible to the AI crawlers, late for Google
HybridBuild or server for the content, browser for the interactionThe whole page, with interactivity added afterThe usual answer for a product site

A worked example. On yourproduct.com, the marketing pages, the guides and the pricing page are static or server rendered, so they exist in the HTML. The dashboard behind the login is client rendered, because no crawler should see it anyway. Nothing is lost and nothing waits for a second pass.

What rendering does to your speed scores

A page built in the browser pays twice: once for the content that arrives late, once for the main thread that is busy while it arrives. Both show up in the numbers Google watches.

  • Largest Contentful Paint: the big element cannot paint until the data arrives. Good is up to 2.5 s, poor above 4 s.
  • Interaction to Next Paint: hydration blocks the main thread while a visitor is already tapping. Good is up to 200 ms, poor above 500 ms.
  • Total Blocking Time: the lab stand-in for the same problem, and the heaviest single item in the Lighthouse performance score.

Server rendering does not fix a heavy bundle. It moves the content earlier, which fixes what a crawler sees and helps the paint. The bundle still has to shrink for the interaction to feel right.

Why the AI crawlers make this urgent

Assistants answer from an index, and the crawlers that build those indexes fetch your HTML without executing it. A page that renders in the browser is not a page they can quote, however well it ranks in Google.

  • OAI-SearchBot, for the pages ChatGPT search cites.
  • Claude-SearchBot and Claude-User, for Claude.
  • PerplexityBot and Perplexity-User, for Perplexity.
  • Bingbot, which also feeds Microsoft Copilot.
  • Googlebot, which does render, and is the only one of the group that does.

The checklist for a JavaScript site

  1. Move the content to the server

    Static or server rendering for every page you want found: the home page, pricing, the guides, the product pages. Keep the browser for what is behind a login.

  2. Make every route a real anchor

    Href attributes with real paths, no hash routes for content, no click handlers standing in for links. Your framework's link component does this; a styled div does not.

  3. Unblock your scripts and styles

    Check robots.txt does not disallow the folders your page needs to render. Google has said for years that blocking them breaks rendering.

  4. Render the metadata too

    Title, description, canonical and structured data belong in the server's HTML. A title written by a script after load is a title Google may never use.

  5. Re-test the page

    URL Inspection, Test live URL, View tested page. The content should now be in the source, not only in the render.

  6. Watch the indexing report

    Pages stuck on crawled and not indexed often move once the content is in the first wave. Give it a couple of weeks before judging.

A fixed page looks like this: view-source on /pricing shows the plan names, the prices and the FAQ text; the AI crawlers can read it; and the interactive parts, the toggle and the checkout button, still work exactly as before.

Questions

Check my site, free

Paste your URL and the free check reads your site the way a crawler does, in about thirty seconds, and says whether your words are in the HTML or waiting on a script.

  • Free check, no card
  • Read-only, your own accounts
  • Readable by your agent

Read next