Your React site ranks for nothing. The pages are there. They’re just invisible to Googlebot at the moment it matters.
JavaScript SEO is the discipline of making sure search engines can crawl, render, and index content that depends on client-side scripts. It sounds straightforward — Google has rendered JavaScript for years, and the Googlebot engine is now evergreen Chromium. In practice, however, an Onely study of thousands of websites found that a large share of JavaScript-rendered content never gets indexed, and client-side rendered sites consistently lag server-rendered equivalents in organic visibility. That gap is not because Google “can’t do JavaScript.” It’s because rendering has costs, queues, and failure modes most teams ignore until traffic flatlines.
This guide explains how search engines actually process JavaScript in 2026, what each rendering model costs you for SEO, and how to spot the failure patterns that quietly delete pages from the index. The focus is conceptual — frameworks come and go, but the underlying mechanics of crawl, render, and index have been stable for years and will stay that way. Understand the mechanics and the framework-specific advice writes itself.
If you’ve spent any time on JavaScript SEO debates, you’ve heard contradictory claims: “Google handles SPAs fine,” “SPAs kill rankings,” “SSR is mandatory,” “SSR is overkill.” All of them are right in some contexts and wrong in others. The deciding factor is rarely the framework — it’s whether the rendered output actually contains the content you want indexed, at the moment the crawler asks for it.
How Search Engines Actually Process JavaScript
Google treats JavaScript pages as a three-stage pipeline: crawl → render → index. A Googlebot request hits your URL, the raw HTML response goes into a render queue, a headless Chromium instance executes the JavaScript, and only then does the rendered DOM feed the index. The official Google Search Central documentation on JavaScript SEO basics spells out the same three stages.
For years this was called “two waves of indexing” — index the raw HTML, then index again after rendering. Martin Splitt from Google has since walked that framing back. As he put it on the Onely blog, “two-wave indexing — or the two waves of indexing — play less and less of a role,” and his current rule of thumb is that 99% of pages render within minutes of being crawled. The pipeline still exists; it’s just less of a queue and more of a continuous flow. For practical JavaScript SEO purposes, you can treat crawl and render as near-simultaneous most of the time, with the caveat that “most of the time” is doing serious work in that sentence.
That said, “minutes” assumes everything goes right. When rendering fails — bad scripts, blocked resources, slow APIs — Google falls back on the raw HTML response. If the raw HTML is a near-empty <div id="root"></div>, that’s literally what gets indexed. The page exists in Search Console. It just contains nothing rankable. Worse, the failure is silent: Search Console will report the URL as indexed, the impression count will sit near zero, and the engineering team will swear the site “works.”
The other thing to understand: rendering is expensive. Google’s web rendering service runs evergreen Chromium across billions of URLs. Pages that render fast, with predictable network calls, get processed quickly. Pages that need 4MB of JavaScript, three rounds of API fetches, and a hydration step get deprioritized. This isn’t documented as a ranking signal — it’s just queue economics. Slow renders compete for the same resources, and yours can lose. Every JavaScript SEO audit eventually circles back to this point: rendering capacity is finite, and complex pages eat more of it.
For non-Google crawlers the situation is more brutal. Bing renders JavaScript inconsistently. According to public statements from the Bing Webmaster team, dynamic rendering or pre-rendered HTML is still the recommended path for JavaScript-heavy sites. AI crawlers — ChatGPT, Perplexity, Claude, Gemini’s grounding bot — overwhelmingly fetch raw HTML and skip JavaScript execution. If your distribution strategy includes AI Overviews or LLM citations, you cannot rely on client-side rendering, full stop.
The Four Rendering Models (and What Each Costs You for SEO)
Modern web apps usually fit one of four rendering strategies. The web.dev “Rendering on the Web” guide by Addy Osmani and Jason Miller remains the canonical reference. Here’s how each one looks from a search engine’s perspective:
| Model | What ships | SEO pros | SEO cons | Best fit |
|---|---|---|---|---|
| CSR (client-side rendering) | Empty shell HTML + JS bundle | Cheap infrastructure; works once rendered | Depends on render queue; raw HTML is empty; brittle to JS errors | Logged-in apps, dashboards, anything not meant to rank |
| SSR (server-side rendering) | Fully populated HTML per request | Crawlers index immediately; resilient to render failures | Server cost; TTFB can suffer under load | Frequently updated content: news, e-commerce, listings |
| SSG (static site generation) | Pre-built HTML at deploy time | Fastest possible TTFB; predictable; cheap to host | Stale content until rebuild; awkward for personalization | Marketing pages, docs, blogs, landing pages |
| Dynamic / ISR | Pre-built but rebuilt on a schedule or signal | Combines SSG speed with freshness; good for large catalogs | Cache invalidation complexity; tooling-specific | Mid-sized e-commerce, large content libraries |
One nuance: dynamic rendering used to mean “serve pre-rendered HTML to bots, JS bundle to humans” via User-Agent sniffing. Google has formally deprecated that approach as a long-term solution. The modern dynamic story is more like ISR or edge rendering — same HTML to everyone, regenerated on cadence. Treat user-agent forking as a temporary patch, not a strategy.
Hybrid approaches dominate in practice. A typical setup ships SSR or SSG on first paint, then hydrates with JavaScript to add interactivity. Done well, the crawler sees full content immediately and users get app-like behavior. Done badly, hydration mismatches strip content from the DOM, the page flickers, and Core Web Vitals collapse. The Core Web Vitals 2025 guide covers why hydration costs show up directly in INP and LCP scores.
Where teams get into trouble is treating the rendering decision as a single global setting. Modern frameworks make per-route choices easy: SSG your blog, SSR your product pages, CSR your account dashboard. The architecture matches the SEO requirement instead of fighting it. JavaScript SEO outcomes improve dramatically when the routing layer reflects which URLs need to rank.
Why “Googlebot Can Render JavaScript” Misleads So Many Teams
The headline is true. The implication is not. Googlebot has rendered JavaScript reliably since at least 2019, when Google moved from Chrome 41 to evergreen Chromium, gaining over 1,000 modern web features in one update. So why do JS sites still struggle to rank?
Three reasons keep showing up in audits:
- Render success is not render quality. Google rendering a page does not mean it indexed every piece of content. Resources that load after the initial render — lazy-loaded sections, infinite scroll, modal-triggered content — often don’t make it into the indexed DOM snapshot. The rendering service has a budget, and your slow-loading footer carousel may simply be cut off.
- Other crawlers are not
Googlebot. Bing renders JavaScript inconsistently. AI crawlers (ChatGPT, Perplexity, Claude) overwhelmingly fetch raw HTML and skip JS execution entirely. If your strategy depends on showing up in AI Overviews, ChatGPT citations, or alternative search engines, CSR is a wall. - Rendering is asynchronous, ranking is competitive. Even if your page renders perfectly within minutes, your competitor’s server-rendered page was indexed in seconds and got linked, clicked, and refined into the index first. For time-sensitive content — news, product launches, trends — minutes is forever.
The honest summary: Google can render JavaScript. Whether it will render your JavaScript correctly, completely, and quickly is a question of your specific implementation, your competitors, and your luck on render-queue day.
This is why pre-rendered HTML — whether SSR or SSG — remains the safest default for content that needs to rank. You stop relying on a black-box render queue and start handing crawlers what they need on the first request. Pre-rendering also makes everything else in the JavaScript SEO stack easier: schema markup parses reliably, internal links are crawlable on the first hit, and meta tags exist before any script runs.
Common JavaScript SEO Pitfalls (with diagnostic patterns)
The same handful of issues account for most JS SEO disasters. Here’s what to look for and how the pattern shows up in tools:
- Empty raw HTML. View source on your page. If the body is a single
<div id="root">with no content, the crawler’s first response contains nothing rankable. Confirm by fetching withcurland aGooglebotuser agent. If the rendered DOM is full but the raw HTML is empty, you’re 100% dependent on the render queue. - Blocked resources in
robots.txt. Ifrobots.txtdisallows/static/js/,/api/, or your CDN host,Googlebotcannot fetch the scripts needed to render your content. Use Google Search Console’s URL Inspection tool, look at “More info → Page resources,” and verify nothing critical is blocked. - JavaScript errors that break the render. A single thrown exception can halt React or Vue from mounting. Crawlers don’t have a console; they just see a broken page. Test in an incognito browser with the network throttled to “Slow 3G” and check for errors.
- Render-blocking API calls. Content fetched after page load — pricing, product specs, author bios — often never makes the indexed DOM. If it matters for SEO, it should be in the initial HTML, full stop.
- Links built as
onClickhandlers. Crawlers follow<a href="…">tags. They do not click buttons. If your navigation is JavaScript-only, internal link graph dies. Audit by disabling JavaScript and checking which links remain functional. - Lazy-loaded content below the viewport. If sections only appear after a scroll event, crawlers may never trigger them. Use the IntersectionObserver pattern carefully, and verify with the URL Inspection rendered DOM.
- Hydration mismatches. When SSR HTML and client-rendered HTML disagree, frameworks often blank out the section and re-render from scratch. The crawler may catch either state, leading to inconsistent indexing.
- Single-page apps with no URL changes. If your “pages” are JavaScript state swaps on one URL, you have one indexable URL no matter how much content exists. Use the History API and unique URLs per view.
- Soft 404s from client-side routing. A failed product fetch that displays “Not found” while the server returns HTTP 200 is a soft 404. Google may eventually deindex these but will treat them as duplicates first. Return real
404status codes from the server.
The diagnostic that catches most of these in one shot: fetch your URL with curl --user-agent "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", then run the same URL through Search Console’s URL Inspection tool. Compare the raw response to the rendered DOM. The delta tells you exactly what depends on JavaScript. For deeper coverage, walk through the technical SEO audit checklist — JS-rendering issues sit alongside the same categories as crawl, indexability, and internal linking.
One pattern worth flagging because it’s so common: a site passes the URL Inspection rendered-DOM check on the homepage and the team declares JavaScript SEO “fine.” Two months later, traffic to product pages cratered because those routes have a different rendering setup and nobody tested them. Spot-check at least one URL per template — homepage, category, detail, blog post, search results — before declaring victory. Different templates often have different rendering paths.
When Client-Side Rendering Is Actually Fine
CSR gets a bad reputation in SEO circles, but it’s the right call more often than purists admit. Use it when:
- The page is behind authentication. Dashboards, account pages, admin panels — none of these should rank. Render them however your team prefers.
- The content is genuinely personalized. A user’s cart, recommendations, or activity feed has no SEO value. Server resources spent rendering them are wasted.
- The application is a tool, not content. Calculators, configurators, single-purpose utilities — the homepage and a marketing landing can be SSR, the actual app can be CSR. Two stacks, one site.
- You’re targeting AI assistants over search engines. A counterintuitive case: AI agents that browse with their own headless browser can handle CSR fine. If your audience uses ChatGPT’s agent mode or Claude’s computer use, the rendering economics shift.
The mistake is using CSR for marketing pages, blog posts, product detail pages, category pages, or anything else that competes for organic traffic. For those, the cost-benefit collapses. You save a few hours of build setup and lose months of ranking velocity.
When You Need SSR, SSG, or Dynamic Rendering Instead
If the page needs to rank, ship pre-rendered HTML. Which flavor depends on how often the content changes:
- Static (SSG). Marketing pages, documentation, blog posts, evergreen landing pages. Build at deploy time, serve from CDN, get sub-100ms TTFB. This is the safest path for content-driven sites. The trade-off — rebuilds on every change — is rarely a real problem if your CI is sane.
- Server-side (SSR). E-commerce product pages with live inventory, news sites with breaking updates, listing pages with filters. Render per request so the HTML always reflects current state. Pair with a smart CDN cache.
- Dynamic / ISR. Large catalogs with mostly stable content. Pre-build at deploy, regenerate individual pages on a schedule or on first-after-change request. Best of both worlds when you have 50,000 product pages and only update a few dozen daily.
One observation from auditing JavaScript-heavy sites: the right answer is almost always “pre-render the index pages and category pages, SSR the detail pages, CSR the user-only sections.” Mixing strategies inside one app is normal and healthy. The mistake is picking one rendering model and forcing every URL to fit it.
The other angle worth keeping in mind: rendering choice interacts with everything else in your SEO stack. Schema markup needs to be in the initial HTML if you want it parsed reliably — see the schema markup guide for why structured data matters more in 2026. On-page elements like titles, meta tags, and headings need to exist before render, not after — the on-page SEO guide covers the full ranking-element checklist. And your keyword research targeting is wasted if the keywords only appear in JavaScript-injected content.
How to Audit JavaScript SEO Without Specialized Tools
You don’t need expensive crawlers to find the worst JS SEO issues. Three free patterns catch most of them:
1. The raw vs rendered comparison
Fetch the page two ways. First, with curl imitating Googlebot:
- Hit the URL with a
User-Agentstring for Googlebot and save the raw HTML response. - Open the same URL in a regular browser, view source, and copy the rendered DOM from DevTools.
- Diff the two. Anything in the rendered DOM but missing from the raw HTML depends on JavaScript execution to exist.
That delta is your JS-dependent surface area. The bigger it is, the more you’re betting on the render queue.
2. The JavaScript-disabled walk
In Chrome DevTools, open Settings → Debugger → Disable JavaScript, then refresh the page. Try to navigate. Can you reach product pages from the homepage? Do category links work? Is the main content visible? If the answer to any of those is “no,” crawlers and AI bots are hitting the same wall.
3. The Search Console rendered DOM check
Use Search Console’s URL Inspection tool, click “Test live URL,” and inspect the rendered HTML and screenshot. Look for empty sections, missing prices, missing reviews, missing internal links. If Googlebot‘s render shows less than your browser shows, you have a render gap. Common causes: third-party scripts blocked by CSP, API calls that time out at the rendering service, or content gated behind user interaction.
Once you’ve found a gap, the fix is usually one of three things: move the content into server-rendered HTML, switch the affected route to SSR/SSG, or simplify the script execution path so the render budget is enough. There’s no clever workaround — Google will not wait an extra ten seconds for your page to finish loading.
JavaScript SEO and the Broader Picture
Rendering is one input among many. Even a perfectly indexed JavaScript site needs the rest of the SEO foundation to compete. External signals like backlinks still drive authority. E-E-A-T factors still decide ranking in YMYL categories. And core performance metrics — Core Web Vitals especially — overlap heavily with the same hydration and bundle-size issues that hurt rendering.
The pattern across all of these: JavaScript-heavy implementations have to do extra work to stay competitive on the basics that simpler sites get for free. That’s not a reason to abandon modern frameworks. It’s a reason to use them deliberately, with pre-rendering on the routes that matter for organic traffic.
One practical consequence: when you scope a JavaScript SEO audit, scope it broadly. Rendering issues rarely live alone. A site with a CSR-by-default architecture usually also has lazy-loaded internal links, JS-injected canonical tags, schema markup that fires after hydration, and a sitemap that lists URLs the crawler can’t reach. Fix one and you’ve shifted the problem, not solved it. Address the rendering layer and the related symptoms tend to fall in line together.
The flip side is also true: if your raw HTML is solid, half the “JavaScript SEO problems” you’ve read about don’t apply to you. Whether your stack is Next.js with SSR, Astro with islands, plain WordPress, or a hand-rolled static generator, what matters to Googlebot is the bytes on the wire. Frameworks are an implementation detail. The output is what gets indexed.
Bottom Line
JavaScript SEO failures rarely look like SEO failures. They look like “the developer team shipped on time” and “the dashboard is fast” and “we’ll fix the ranking issue next quarter.” By the time anyone connects flat organic traffic to a CSR rollout, six months of compounding ranking loss has happened.
The fix is not to fear JavaScript. It’s to be honest about which rendering model each URL deserves. Content that needs to rank gets pre-rendered HTML — SSR or SSG, sometimes ISR. Content that doesn’t need to rank can use whatever stack the team prefers. Audit the gap between raw HTML and rendered DOM regularly. Treat Googlebot‘s “I can render JavaScript” as a capability, not a guarantee.
Three habits separate teams that get JavaScript SEO right from teams that don’t. First, they pick rendering per route rather than per app. Second, they run the raw-versus-rendered diff on every major template before launch. Third, they re-test after each significant deploy, because a bundle change or a third-party script update can silently break the render pipeline. None of this is exotic. It’s standard JavaScript SEO hygiene applied consistently.
Done deliberately, JavaScript SEO is a solved problem. Done by default, it’s a quiet drain on traffic that nobody notices until it’s too expensive to fix. Pick the rendering model that matches the goal of each URL, verify what crawlers actually see, and the rest of your SEO work has a foundation to stand on.