If you build with React, Vue, or any JavaScript framework and you care about organic traffic, there's a good chance you're still optimizing for a Google that no longer exists.
For years the mental model was "two waves of indexing": Google reads your HTML in wave one, then comes back days or weeks later to run your JavaScript in wave two. That model — which Google itself presented back in 2018 — has quietly disappeared from the official documentation. In 2026, rendering is an asynchronous queue, and Google's own docs now say a page "may stay on this queue for a few seconds, but it can take longer than that." The famous "5-second JS execution limit"? Also not in the docs. It was never a hard rule.
So the interesting question in 2026 isn't when Google will run your JavaScript. It's what Google actually kept after it rendered your page — because that's a very different thing, and it's where most JS sites quietly bleed traffic.
How Google actually processes a JS page now
Three stages, in order: crawl → render → index.
-
Crawl. Googlebot makes an HTTP request, checks
robots.txt, and parses whatever HTML comes back. -
Render. Every
200page gets queued for rendering in a headless, evergreen Chromium (it understands modern JS — ES2020+, dynamicimport(), even WebAssembly). Syntax support is not your problem. Resource and queue constraints are. - Index. Google indexes the rendered DOM and re-extracts links from it.
One trap worth memorizing: if a noindex is present in the initial HTML, Google may skip rendering entirely. So the classic "we'll inject the right robots tag with JavaScript" plan can backfire — the script that was supposed to remove the noindex never runs.
The failure modes nobody warns you about
Here's the uncomfortable part. A client-side-rendered (CSR) page ships an almost-empty HTML shell; everything real arrives with the JS. When rendering succeeds and the content looks valuable, the page indexes fine. When it doesn't, you land in one of these buckets — all visible in Search Console:
| Symptom | Typical status | Likely cause |
|---|---|---|
| HTML is a shell, content from JS | "Crawled – currently not indexed" (or indexed empty) | Render failed or judged thin |
| Client-side routing, empty states served as 200 | "Soft 404" | No real error code |
| Large JS site, new pages pending | "Discovered – currently not indexed" | Render queue / crawl budget |
| title/canonical/robots injected via JS | Duplicate / canonical conflicts | Initial HTML disagrees with the render |
The most insidious is the indexed-empty shell: the URL is in the index, but it ranks for nothing because Google kept the pre-JS shell. Nothing looks broken until you compare what got indexed with what should have.
And "Soft 404" is a pure SPA disease: your router shows a "no results" view but the server still returns 200. Sold-out products, empty search pages, and misconfigured fallback routes generate these at scale.
CSR vs SSR vs SSG vs ISR, minus the dogma
You don't need to server-render everything. You need SEO-critical content in the initial HTML response. Quick decision:
- CSR — browser builds the DOM. Fine for dashboards behind a login; risky for anything you want ranked. Pure CSR can delay indexation enough to cost a large share of organic traffic.
- SSR — HTML per request. Best for dynamic or personalized content (e-commerce, feeds).
- SSG — HTML at build time, served from a CDN. Ideal for blogs, docs, landing pages. Fastest TTFB.
- ISR — SSG that revalidates in the background. The pragmatic 2026 default: static speed, fresh-enough data.
The non-negotiable, regardless of framework: your title, meta description, H1, canonical, internal links, and structured data must exist in the server HTML — not be painted in afterward. That's what lands cleanly in the index with zero dependency on a successful render.
A few concrete gotchas:
- Use the History API for SPA routing, not URL fragments — Googlebot can't reliably resolve
#/routes. -
Fingerprint your assets (
main.2bb85551.js). Googlebot's Web Rendering Service caches aggressively and may serve stale JS/CSS otherwise. - Web components get flattened (shadow + light DOM), but only what's in the rendered HTML is indexable.
Stop trusting your own crawler
This is the mistake I see most. Teams run Screaming Frog or Sitebulb in render mode, see their content, and conclude "we're fine." But that only proves your tool rendered it — with its engine, its timeouts, its settings. It says nothing about what Google rendered or chose to keep.
The source of truth is Search Console:
- URL Inspection shows Google's own rendered HTML, the resources it loaded, and any JS exceptions it hit.
- The Page Indexing report gives the verdict per URL, at scale.
On a big JS site the real question isn't "is this one URL rendered right?" — it's "what share of my JavaScript-dependent pages is actually indexed, and under which exclusion reasons?" You answer that by bulk-inspecting your sitemap URLs against the official inspection data, not by re-rendering locally.
A 2026 checklist
- [ ] SEO-critical content (title, H1, canonical, links, schema) is in the initial HTML
- [ ] No
noindexin the served HTML that you intend to flip with JS - [ ] SPA routing uses the History API; missing pages return a real
404server-side - [ ] Assets are content-fingerprinted
- [ ] You verify indexation in Search Console, not your own crawler
- [ ] Anything ranking-critical uses SSR/SSG/ISR, not pure CSR
None of this requires abandoning your framework. It requires being deliberate about where your HTML is generated for the pages that need to rank.
If you'd rather have someone pressure-test your stack, this is exactly the kind of thing a technical SEO team digs into — and running a free SEO audit is a fast way to see which of your JS pages Google is quietly dropping before you refactor anything.
Written by Garvit Sharda. I work with COM8 STUDIO, a digital marketing and web agency, on technical SEO and front-end performance.
Top comments (0)