JavaScript SEO

Is Googlebot Actually Seeing What Your React or Next.js Site Renders?

JavaScript SEO means closing the gap between what a browser renders after your JS runs and what a crawler indexes. On a client-rendered or partially-hydrated site, those two can diverge — content that's visible to a visitor can be invisible, delayed, or truncated for the crawler that decides whether the page ranks at all.

Dfeelings Team · Digital Marketing Agency, Amman, Jordan
Last updated: September 18, 2026

The problem

Most technical SEO advice assumes the HTML a server sends is the content search engines see. On JavaScript-rendered sites that assumption breaks: Googlebot has to execute your JS in a second rendering pass before it can index anything client-rendered, that pass is queued and can be delayed by days on a large or infrequently-crawled site, and any JS error, unhandled promise rejection, or blocked resource during that pass can silently drop content Google never retries. A page can look complete in a browser and still be indexed as an empty shell.

Symptoms

  • Google Search Console's URL Inspection tool shows a rendered screenshot that's missing content visible in a normal browser, or the "Page fetch" step succeeds while "Screenshot" or "More info" flags render-blocked resources.

  • The indexed content of a page (checked via `site:` search or the Inspection tool's rendered HTML) lags noticeably behind what's currently live, even weeks after a content update.

  • Content behind a client-side fetch (data loaded after mount, e.g. `useEffect` + `fetch`) never appears in Google's cached/rendered HTML at all.

  • Internal links rendered only via `onClick` handlers or JS-injected `<a>` tags without a real `href` aren't discovered by crawlers that don't execute every event handler.

  • Core metadata (title, canonical, meta description) is set client-side after hydration instead of present in the initial server response, so it's inconsistent between what curl/view-source shows and what the browser eventually renders.

How we diagnose it

We start with the same test that matters most: fetch the URL the way a crawler does (no browser, no JS execution — plain HTTP GET) and diff that against the fully rendered DOM in a headless browser. Anything present in the rendered DOM but absent from the raw response is a candidate for a render-dependent indexing gap. We cross-check with Search Console's URL Inspection rendered screenshot and "View Crawled Page" HTML, check the Network tab for render-blocking or failed requests during first paint, and review Core Web Vitals field data (see our Core Web Vitals page) since slow hydration and JS SEO problems frequently share a root cause.

The fix

The fix is almost always to move content into the initial server response rather than leaving it dependent on client-side JS execution — server-side rendering, static generation, or streaming server components for anything that matters for indexing (headings, body copy, canonical/meta tags, internal links with real hrefs). Content that's genuinely interactive-only (a dashboard behind auth, for example) doesn't need this; content meant to rank does. We also make sure critical resources (fonts, above-the-fold images, render-blocking scripts) aren't accidentally disallowed in robots.txt, since a blocked resource during Google's rendering pass can cause the page to be indexed as incomplete even when the HTML itself is fine.

Implementation details

This site is itself built on Next.js's App Router: every canonical page under `src/app/[lang]/[...slug]/page.tsx` is a Server Component whose `generateMetadata()` resolves title, description, canonical and hreflang tags server-side before any client JS runs — the same pattern this page describes rather than something we only recommend to clients. Page bodies then hand off to client components (the `"use client"` files under `src/components/PageContent/`) only for interactivity (animations, accordions), not for the text content itself, which arrives in the initial HTML via the translations JSON consumed server-side. For Next.js sites specifically we check: the App Router's static vs. dynamic rendering choice per route (`generateStaticParams` coverage), whether client components are hydration-only wrappers around server-rendered content versus a client-rendered island that swallows content until JS loads, and whether `next/dynamic` imports used for code-splitting (see the `dynamic-loaders` pattern used throughout this codebase) still render server-side (`ssr: true`) rather than client-only.

Common mistakes

  • Treating "it renders fine in Chrome DevTools" as proof it's indexable — that only confirms the browser can execute the JS, not that Google's rendering pass will pick it up on the same schedule or budget.

  • Fetching primary page content client-side (`useEffect` + API call) instead of server-side, so there's a real chance a crawler indexes the loading state instead of the content.

  • Using `next/dynamic(..., { ssr: false })` for content that matters for SEO, which opts the whole chunk out of server rendering, not just its interactivity.

  • Blocking JS/CSS bundles in robots.txt (a legacy practice from the pre-rendering-crawler era) that actively prevents Google from rendering the page correctly today.

  • Assuming a JS framework migration (e.g. moving to a new meta-framework or app architecture) preserves SEO by default — rendering strategy, metadata generation, and route structure all have to be re-verified, not assumed.

Validation

We validate with Search Console's URL Inspection tool (both the rendered screenshot and "View Crawled Page" HTML) on a sample of real URLs, a raw-HTTP-vs-rendered-DOM diff across the site's main templates, a robots.txt check for accidentally blocked JS/CSS, and Core Web Vitals field data to confirm the fix didn't just move content into the DOM but also kept it fast enough for INP.

Bilingual JS SEO considerations

On a bilingual EN/AR site, JavaScript SEO issues can compound with locale routing: content fetched client-side after a locale switch, or hreflang alternates computed only in a client effect, can leave one language's rendered response incomplete even when the other locale's is fine. We verify both locales' raw HTTP responses independently, not just one and assume symmetry.

Related service & solutions

JavaScript SEO is the rendering layer underneath schema markup, hreflang, and canonical tags — none of those signals matter if the content and metadata that carry them never reach the crawler intact.

Frequently asked questions

Common questions about JavaScript SEO

Google does execute JavaScript, but that rendering pass is a separate, resource-constrained second step after the initial crawl — it can be delayed, it can fail on errors that a browser silently recovers from, and it doesn't guarantee the same outcome as loading the page in Chrome yourself. "Google can render JS" is not the same claim as "your specific page renders correctly, quickly, and consistently in Google's pipeline."
For content that needs to be indexed and ranked, yes — SSR, static generation, or server components ensure the crawler's first pass already has the content. For genuinely interactive-only features behind authentication or user action, client rendering is fine, since there's no indexing goal for that content.
Usually. Most JS SEO issues are fixable within the existing framework — changing which components render server- vs. client-side, fixing metadata timing, unblocking resources in robots.txt — rather than requiring a full rewrite. We diagnose the specific gap before recommending anything that large.
Core Web Vitals is about how fast and stable the page is once it loads. JavaScript SEO is about whether the content is indexable at all. They frequently share root causes (heavy client-side rendering hurts both), which is why we diagnose them together, but fixing one doesn't automatically fix the other.

Want your JavaScript rendering checked against what Google actually indexes?

Request a JavaScript SEO audit