Skip to content
Metevix
Technical SEO8 min read

JavaScript SEO: How to Diagnose and Fix the Rendering Gaps That Hide Your Content

Why Googlebot indexes an empty page, how to tell client-side rendering problems from crawl problems, and the exact tests that separate the two.

PR

Priya Raman

Principal Engineer, Crawl Systems · Published

A page can return `200 OK`, appear in your sitemap, be linked from your navigation, and still be indexed as a blank document. When that happens the problem is almost never the crawl. It is the render.

This guide is the diagnostic sequence we run, in order, and what each result rules out.


1. The Two-Wave Model, and Why It Bites

Googlebot processes JavaScript sites in two passes. The first wave fetches raw HTML and indexes whatever text is present. The page then enters a render queue, where a headless browser executes JavaScript, and a second wave indexes the result.

The gap between waves is not guaranteed. It has been observed at minutes and at weeks. Everything that exists only after hydration is invisible for that entire window.

For most AI retrieval bots there is no second wave at all. They read the first response and leave.

<!-- First-wave HTML for a client-rendered product page -->
<div id="app"></div>
<script src="/assets/index-4f2a1b.js" defer></script>
<!-- Indexable words: 0 -->

2. Test One: What Does the Raw Response Contain?

Before opening any tool, ask what the server actually sent. `curl` does not execute JavaScript, which makes it an exact simulation of a non-rendering bot:

# Does the H1 exist before JavaScript runs?
curl -s https://example.com/products/widget | grep -i "<h1"

# How much text is really in the first response? curl -s https://example.com/products/widget | sed 's/<[^>]*>//g' | wc -w ```

If the word count is under roughly 50, you have a rendering problem and can stop reading tooling reports. If the content **is** present, the problem is elsewhere — canonicals, directives or internal linking.


3. Test Two: Rendered vs Raw, Side by Side

The useful measurement is the *delta*. Compare the raw response against the fully hydrated DOM, in DevTools on the live page:

document.body.innerText.split(/\s+/).length

Divide the raw count by the rendered count.

| Raw / Rendered | Interpretation | | :--- | :--- | | Above 0.9 | Server-rendered. Healthy. | | 0.6 to 0.9 | Partial hydration; check tabs, accordions, lazy sections. | | Below 0.6 | Client-rendered. Most retrieval bots see nothing useful. |


4. Test Three: URL Inspection, Rendered HTML Tab

Search Console's URL Inspection tool shows Google's *own* rendered output. Use **Test Live URL**, then open the **Rendered HTML** tab — not the screenshot, which hides text-level failures.

Look specifically for:

  • Content present in your browser but absent in Google's render — usually a JavaScript error, or an API call that fails from Google's IP ranges
  • `noindex` injected client-side, which the first wave never sees and the second wave obeys
  • Infinite-scroll content with no paginated URL behind it

5. The Fixes, In Order of Leverage

**Server-render the primary content.** In the Next.js App Router this is the default. The regression is almost always an unnecessary `"use client"` at the top of a layout or page, which drags the whole subtree client-side. Audit for it:

grep -rl '"use client"' app/ | head -30

Push that directive down to the leaf that genuinely needs interactivity — a form, a carousel — and leave the text on the server.

**Never gate content behind interaction.** Tabs and accordions are fine; content that only *exists* in the DOM after a click is not. Render it and hide it with CSS.

**Give infinite scroll real URLs.** If page two has no address, it cannot be indexed or linked.

**Fetch on the server.** A client-side fetch from an origin Google cannot reach, or one rate-limited against a datacentre IP, renders an empty page with no visible error.


6. Rendering Is a Performance Problem Too

Every fix above also removes JavaScript from the critical path, which moves LCP and INP in the right direction. The two problems share a cause and share a solution — see [field data vs lab data](/blog/field-data-vs-lab-data-crux-lighthouse) for why your Lighthouse score may not reflect it yet, and [crawl budget waste](/blog/how-to-fix-crawl-budget-waste-and-orphan-pages) for what happens when renderable pages still never get crawled.

[Run a Metevix audit](https://metevix.com/audit) to get the raw-versus-rendered ratio for every template on your site in one pass.

Instant Website Intelligence

Audit your site for this issue right now.

Metevix crawls your site, ranks what to fix by real impact, and verifies every fix automatically.