GUIDE / TECHNICAL SEO

JavaScript SEO: Rendering, Hydration and What Googlebot Actually Sees

Google can render JavaScript. That sentence has caused more indexing problems than it has solved, because 'can' is not 'always, immediately and completely'. This guide explains how rendering works, which architecture choices make it reliable, where content and links go missing, and how to test what Googlebot actually sees.

By Maharshi KushwahaUpdated 8 min read
On this page
  1. How Google processes a JavaScript page
  2. Rendering architectures, and what each means for SEO
  3. Hydration: where content quietly changes
  4. Where content and links go missing
  5. Testing: rendered HTML versus raw HTML
  6. Framework notes
  7. JavaScript SEO checklist
  8. Frequently asked questions
  9. Where this connects

Every few months I get a variation of the same message: "We rebuilt the site on a modern framework, it's much faster, and organic traffic has dropped." The site is usually fine for users. It's often not fine for Googlebot, and the reasons are specific and fixable. This sub-guide of my technical SEO guide covers what happens between a JavaScript-heavy page and Google's index.

How Google processes a JavaScript page

Googlebot processes a page in two passes. First it reads the raw HTML as served: whatever text, links, meta tags and canonicals exist without executing any script. Those links are queued and that content can be indexed immediately. Then the page goes into a render queue, where a headless Chromium instance executes the JavaScript and Google indexes whatever the rendered DOM contains.

  1. Fetch raw HTML
  2. Index what's there, queue links
  3. Render queue
  4. Execute JavaScript
  5. Index rendered DOM
  6. Discover rendered links

Three things about the second pass matter. It is not instant; it can happen seconds or days later. It is not guaranteed to complete; scripts that error, time out, depend on blocked resources or need user interaction simply don't produce content. And it is costly, so on large sites Google renders a fraction of URLs per visit. If everything important is only in the rendered DOM, your whole site sits behind a slow, rationed, failure-prone step.

Rendering architectures, and what each means for SEO

The architecture decision is usually made by engineering before SEO is in the room. Know the trade-offs well enough to argue for the right one.

ApproachWhere HTML is producedWhat Googlebot gets on first fetchSEO riskTypical fit
Client-side rendering (CSR)In the browser, after JavaScript loadsA near-empty shellHigh. Everything depends on the render queue.Logged-in apps, dashboards, anything not meant to rank
Server-side rendering (SSR)On the server, per requestFull HTML with content and linksLow, if hydration doesn't alter contentEcommerce, marketplaces, frequently changing content
Static generation (SSG)At build timeFull HTML from a CDNLowest. Also fastest.Marketing sites, docs, guides, programmatic pages that change rarely
Hybrid (per-route or incremental)Static where possible, server or client where neededDepends on routeLow if ranking routes are server- or statically renderedMost real SaaS and content sites
Dynamic rendering (prerendered version for bots)A prerendering service, bots onlyFull HTML, but a different build from what users seeMedium. A workaround Google now discourages.Legacy CSR apps that can't be rebuilt yet

My default position: anything that needs to rank should be in the raw HTML on first fetch. SSR or SSG for ranking pages, CSR only for the interactive parts behind login. Modern frameworks make this a configuration choice, not a rewrite, which is why "React is bad for SEO" is out of date. The framework isn't the problem. The rendering mode is.

Hydration: where content quietly changes

With SSR, the server sends complete HTML, then JavaScript loads and "hydrates" it, attaching interactivity. In theory the content doesn't change. In practice, I've seen hydration remove content (a component that renders empty until an API call resolves), replace it (client-side personalisation), or inject conflicting head tags. The rule: the rendered DOM should be a superset of the raw HTML, never a different page.

Lazy-loaded content

Content loaded when it scrolls into view, or after a timer, doesn't exist for Googlebot, which doesn't scroll. Product descriptions in a tab that fetches on click, reviews that load on scroll, "load more" sections: all invisible. Native image lazy-loading with loading="lazy" is fine; content that needs an event to fetch is not. If it needs to be indexed, it needs to be in the HTML.

Links that aren't links

Googlebot follows <a href="…"> elements. It does not follow <div onclick>, buttons with router pushes, or anchors with href="#" and a click handler. Single-page-app navigation without real anchors produces a site where every page is an orphan from Google's point of view. Render proper anchors with real URLs and let the router intercept clicks for users. This is a common cause of the "Discovered – currently not indexed" problem in the crawlability guide.

Infinite scroll

Infinite scroll with no paginated fallback means Googlebot sees the first batch and nothing else. Pair it with real paginated URLs (?page=2) that are linked in the HTML and update as the user scrolls. Users get the smooth experience; Googlebot gets a path to every item.

Head tags set by JavaScript

Titles, descriptions, canonicals, robots meta and hreflang set client-side are only seen after rendering, and if the raw HTML holds a different value, Google may use either. Set them server-side. A canonical that only exists post-render is barely a hint; see the canonical tags guide.

Blocked resources

If robots.txt blocks the JavaScript bundle, the API the page calls, or the CDN serving them, the render fails silently. The page looks fine in a browser and empty to Googlebot. It's the first thing I check on a JavaScript site with indexing problems.

Testing: rendered HTML versus raw HTML

You cannot reason about JavaScript SEO; you have to look. Compare what the server sends to what the browser builds, then check what Google says it saw.

  1. Fetch the raw HTML

    Use curl or "view source" (not the DevTools inspector, which shows the rendered DOM). Look for the main heading, a paragraph of body text, the canonical tag and a few internal links. If they're absent, you're relying on rendering.

  2. Inspect the rendered DOM

    Open DevTools' Elements panel and search for the same things. Note anything present here but missing in step 1, and anything that differs (titles, canonicals).

  3. Ask Google

    Run URL Inspection in Search Console, "Test live URL", then "View tested page". Compare the HTML with your rendered DOM and check "More info" for blocked resources and console errors. The screenshot tells you if the page rendered at all.

  4. Crawl with rendering on and off

    Two crawler runs, one with JavaScript rendering disabled and one enabled, show at site scale which pages, links and tags depend on JavaScript. The diff is your issue list.

  5. Check the index

    Search for an exact sentence of rendered-only content in quotes. If Google indexed it, it rendered the page. If not, it either hasn't yet or couldn't.

I ran this comparison as a controlled experiment, measuring how quickly and completely Google indexed identical content served raw versus rendered-only, in the JavaScript rendering test. The gap is why I keep insisting ranking content belongs in the first fetch.

Framework notes

None of the major frameworks is inherently bad for SEO. Each has a well-supported path to server or static rendering.

React / Next.js. Plain React (Vite templates, Create React App) is client-rendered by default. Next.js offers static generation, server rendering and incremental regeneration per route, with server components as the default in the App Router. Common mistakes: fetching ranking content inside client components, and leaving metadata to client-side libraries.

Vue / Nuxt. Same pattern. Vue alone is client-side; Nuxt provides SSR and static generation with sensible defaults. Watch for content behind <ClientOnly> wrappers and head tags set only on the client.

Angular. Angular's SSR support (formerly Angular Universal) handles server rendering. Older builds are often fully client-rendered and need it enabled. Lazy-loaded route modules are fine; content that waits on module load is not.

For SaaS products, the pragmatic structure is often two surfaces: a statically generated marketing and docs site that ranks, and a client-rendered application behind login that doesn't need to. Mixing them in one CSR bundle is how marketing pages end up invisible.

JavaScript SEO checklist

Content in raw HTML

Headings, body text, product details and prices are present on first fetch for every page that should rank.

Real anchors with real hrefs

All navigation, pagination and contextual links are <a href> elements with crawlable URLs.

Head tags server-side

Title, description, canonical, robots and hreflang are in the raw HTML and don't change after hydration.

No blocked resources

JavaScript, CSS, fonts and API endpoints the page needs are allowed in robots.txt on every host they're served from.

No interaction-gated content

Nothing that should be indexed waits for a scroll, click, tab or timer. Infinite scroll has paginated URLs.

Status codes are real

Missing pages return a 404 or 410 at the HTTP level, not a 200 with a "not found" component rendered by JavaScript.

Frequently asked questions

Can Google index JavaScript-rendered content at all?

Yes, usually. Google renders pages in a second pass and indexes the result. The problems are delay, incompleteness and failure: rendering can be deferred, interaction-gated content is missed, and errors or blocked resources break it silently. Server or static rendering removes those risks for pages that need to rank.

Is a single-page application bad for SEO?

Not inherently. An SPA with server-side rendering, real anchor links and server-set head tags works well. One that renders everything client-side with router-driven navigation and no HTML fallback tends to index poorly. The architecture matters more than the label.

Should I use dynamic rendering or prerendering for bots?

Only as a temporary bridge for a legacy client-rendered site that can't be rebuilt yet. It adds infrastructure, creates a bot-versus-user mismatch that's easy to break, and Google now recommends proper server or static rendering instead.

How do I know if Google has rendered my page?

Use URL Inspection's live test and view the rendered HTML and screenshot. For an indexed page, search Google for an exact phrase that only exists after rendering. If it's found, rendering happened; if not, check for blocked resources and console errors.

Does lazy-loading images hurt JavaScript SEO?

Native lazy-loading via the loading attribute is fine. Custom JavaScript lazy-loading that only sets the image source on scroll can hide images from Google. Either way, never lazy-load the main above-the-fold image, which also matters for Core Web Vitals.

Where this connects

This guide sits under the technical SEO hub. Rendering checks are a dedicated section of my technical SEO audit framework; the raw-versus-rendered indexing comparison is the JavaScript rendering test; the SaaS organic growth case study shows what changes when a framework-built marketing site is made properly crawlable; and fixing it is part of my technical SEO services.

Work with me

Need help applying this?

Reading is the easy part. If you want this implemented on your site, tell me what you're working on.

Work With Me Follow the SEO Roadmap
Email — [email protected]
Based in — India · working remotely
Response — usually within 2 working days