Crawl, render, index — and the queue in the middle
For a plain HTML page, Google fetches the URL and has everything at once: headline, copy, links, metadata. One step.
For a JavaScript-dependent page it's two. Googlebot fetches the URL, finds an effectively empty shell, and puts the page into a render queue. Later a headless Chromium runs the JavaScript, produces the DOM, and *that* is what gets indexed. Google says most pages are rendered within minutes of being crawled — but it's a queue with resource limits, and the tail is where the pain lives.
Two consequences. Indexing is delayed, which hurts anything time-sensitive. And links that exist only in the rendered DOM aren't discovered until the render happens, so your deeper pages are delayed by the same queue, recursively.
CSR, SSR and static — what each one ships
The rendering strategy decides what's in that first HTML response, which decides everything else.
- The test is one line:
curl -s https://yoursite.com/page | grep "your headline". If the headline is there, the part that matters is server-rendered. It isn't a framework argument — a React app can be fully server-rendered, and a WordPress theme can hide its content behind a client-side fetch. See Next.js versus WordPress.
| Strategy | First response contains | SEO risk |
|---|---|---|
| Client-side rendering (CSR) | An empty <div id="root"> and a script bundle. | Highest. Everything waits on the render queue; non-rendering crawlers get nothing. |
| Server-side rendering (SSR) | Fully-formed HTML, generated per request. | Low. Watch server response time — TTFB becomes the bottleneck. |
| Static generation (SSG / ISR) | Fully-formed HTML, generated at build or on revalidation. | Lowest. Watch staleness — content can lag the database. |
| Hybrid (server shell, client content) | Layout and nav in HTML; the content fetched client-side. | Deceptive. It looks server-rendered until you search the source for your product description. |
The three views of your page, and which one Google sees
There are three versions of every page, and developers routinely check the wrong one and declare it fine.
- View Source — the raw HTML the server sent. What a non-rendering crawler gets, and what Google gets on its first pass.
- DevTools → Elements — the live DOM after your JavaScript ran in *your* browser, on *your* machine. It always looks great. It proves nothing.
- URL Inspection → Test Live URL → View Tested Page → HTML — the DOM Google's renderer produced. The only authoritative answer, and it's free.
The failures that show up again and again
In practice the same handful of mistakes account for nearly every JavaScript indexing problem we're called in for.
- Links that aren't links.
<div onClick={() => router.push('/product/123')}>renders nohref, so Google finds no link and the destination is discovered only via your sitemap, with no anchor text. Next.js's<Link>emits a real anchor; hand-rolled click handlers don't. - Metadata set after mount. A title written in a
useEffectis absent from the first response and absent for every non-rendering consumer. Emit it server-side. - `ssr: false` on something that matters. Right for a map widget, wrong for your product description — it removes the content from the HTML entirely.
- Content behind an interaction. Tabs and accordions are fine when the content is already in the DOM. When the click triggers a fetch, it doesn't exist for a crawler at all.
Hydration and content parity
Hydration is React attaching its handlers to server-rendered HTML, and it works fine until server and client disagree about what the page says. Personalisation, geo-detection, A/B tests and time-based content all produce a server response and a client render that differ — and Google indexes what its renderer produced, once, from whichever variant it happened to receive.
The rule is boring and it holds: primary content, headings, canonical, metadata and internal links must be identical in the server HTML and after hydration. Vary the banner and the currency toggle. Don't vary the thing you're trying to rank.
One last check, five minutes, no tools: load a key page with JavaScript disabled. Headline, body copy and navigation still there means you don't have a JavaScript SEO problem. A blank page means you do — worth an audit before you spend more on content.