Skip to content

Module 04 · Technical SEO · Guide 6 of 9

JavaScript SEO

JavaScript SEO in 2026: how Google crawls, renders and indexes JS sites, the noindex and canonical traps, and why AI crawlers need server HTML.

By Updated 5 min readIntermediate

What you'll learn

  • Explain Google's crawl, render and index pipeline and what must be in the initial HTML.
  • Choose a rendering strategy (SSG, ISR, SSR, CSR) for content that must rank.
  • Avoid the noindex, canonical and status-code traps Google documented in 2025-2026.
  • Fix links, metadata and soft 404s that only work after client-side JavaScript.
  • Verify what Googlebot and non-rendering AI crawlers actually receive.

Search engines execute JavaScript, but later, at a cost, and not every crawler does it at all. JavaScript SEO is the discipline of making sure content that depends on JS still gets crawled, rendered and indexed reliably. For developers shipping React, Vue or Svelte apps, this is the technical-SEO area most likely to bite you.

How Google processes JavaScript#

Google describes three phases: crawling, rendering and indexing, each with its own queue.

Crawl:  fetch HTML  → check robots.txt, parse <a href> links, read status code and meta tags
Render: headless, evergreen Chromium executes your JS (queued: seconds, sometimes longer)
Index:  the rendered HTML is indexed and its links are queued for crawling

Google says a page "may stay on this queue for a few seconds, but it can take longer than that." The older "two waves, days apart" framing overstates the delay for most sites, but the principle still holds: everything that matters should already be in the initial HTML.

  • Content in the initial HTML is seen on the first fetch, by every crawler.
  • Content that only exists after JS execution depends on rendering succeeding (no timeouts, no blocked resources, no errors).
  • Links that only exist after JS are discovered later, which slows crawling of deeper pages.
  • Googlebot reads only the first 2MB of the HTML, and each JS and CSS file has its own 2MB limit. Giant inline hydration payloads can push your real content or tags past the cutoff (Crawling & Indexing).

On 2026-03-04 Google removed an outdated section of its docs, saying "using JavaScript to load content is not 'making it harder for Google Search'." That is true for Google. It is not true for everyone else.

AI crawlers usually don't render#

Vercel's December 2024 analysis found that none of the major AI crawlers it measured (GPTBot, OAI-SearchBot, ChatGPT-User, ClaudeBot, PerplexityBot, Meta-ExternalAgent, Bytespider) executed JavaScript; Google's Gemini (via Googlebot's infrastructure) and Applebot did. If your content only exists after hydration, ChatGPT, Claude and Perplexity may see an empty shell. That makes server rendering a GEO requirement, not only an SEO nicety. Which of those bots you let in at all is covered in AI Crawlers & Bot Access.

Rendering strategies ranked#

StrategyInitial HTMLSEO reliability
SSG / ISR (prerendered)CompleteBest, and fastest
SSR (per request)CompleteExcellent
CSR (client-only SPA)Empty shellFragile: depends entirely on rendering, invisible to non-rendering bots

The rule: content pages must arrive as server-rendered HTML. In the Next.js App Router this is the default: pages are Server Components, prerendered when possible. You opt into fragility by fetching content with useEffect in client components.

❌ invisible before rendering vs ✅ server-rendered
// ❌ Content exists only after a client-side fetch
"use client";
export default function Post() {
  const [post, setPost] = useState(null);
  useEffect(() => { fetch(`/api/post`).then(/* … */); }, []);
  return post ? <Article post={post} /> : <Spinner />;
}
 
// ✅ Content is in the HTML every crawler fetches
export default async function Post({ params }) {
  const post = await getPost((await params).slug);
  return <Article post={post} />;
}

The 2025-2026 rules Google wrote down#

Between December 2025 and March 2026 Google added several clarifications to its JavaScript docs. None changed how Googlebot behaves, but each one explains a class of bugs:

DateWhat Google documentedWhat to do
2025-12-15When Google sees noindex, it "may skip rendering and JavaScript execution", so JS that removes or changes a noindex "may not work as expected"Never ship noindex in the initial HTML of a page you want indexed
2025-12-17Canonicalization happens before and after rendering. Don't use JS to change the canonical to a different value from the original HTMLSet the canonical in HTML. If you can't, leave it out of the HTML and inject one tag with JS
2025-12-18Pages with a non-200 status "might" skip renderingDon't rely on JS to fill in content on 404 or 5xx responses
2026-03-24Google respects robots meta tags even when placed in the <body>A stray noindex rendered by any component counts

Two older rules still apply: the Web Rendering Service is stateless (cookies are not kept, and localStorage, sessionStorage and service workers are cleared between page loads), and it caches resources aggressively and may ignore your caching headers, so use content-hashed filenames (main.2bb85551.js) to avoid stale bundles.

The classic failure modes#

Crawlers follow <a href>. They do not click divs, and they don't resolve #/fragment routes:

// ❌ undiscoverable
<div onClick={() => router.push("/pricing")}>Pricing</div>
 
// ✅ a real anchor (Next.js <Link> renders one)
<Link href="/pricing">Pricing</Link>

Client-side-only metadata#

Titles, canonicals and robots tags set by client JS arrive late at best, and conflict with the HTML at worst (see the table above). Use the server-side Metadata API so they are in the initial HTML.

Soft 404s#

A SPA that renders "Not found" with HTTP 200 fills the index with junk URLs. Return a real 404: in Next.js, call notFound() from the page. A pure client-side SPA has two documented options: redirect with JS to a URL that returns a server 404, or add <meta name="robots" content="noindex"> to the error view.

Content behind interaction#

Tabs, accordions and "load more" content that is only fetched on click doesn't exist for crawlers. If it must be indexed, render it in the HTML (hidden-but-present is fine) or give it its own URL.

Blocked resources#

If robots.txt disallows the JS bundles or API routes your page needs to render, Google renders a broken page. Let crawlers fetch your assets.

Web components#

Google flattens shadow DOM and light DOM when rendering. Content that doesn't appear in the rendered HTML (for example, light DOM content with no <slot>) is not indexed.

How to verify what bots see#

  1. curl the page: is your content in the raw HTML? This is also what most AI crawlers get.
curl -s https://example.com/post | grep -c "your headline"
  1. URL Inspection in Search Console → "Test live URL" → "View tested page" shows the rendered HTML, a screenshot and any JavaScript console errors: ground truth for Googlebot.
  2. Disable JavaScript in DevTools and reload: whatever survives is what non-rendering crawlers see.
  3. Crawl with JS rendering on and off (Screaming Frog and Sitebulb support both) and diff the discovered links and word counts. The delta is your JS dependency.

Next: International SEO, serving the right language and country variant to the right searcher.

Check your understanding

0 of 4 answered

  1. 1.A product page ships <meta name="robots" content="noindex"> in its HTML, and client-side JavaScript removes the tag once stock data loads. What does Google document about this pattern?
  2. 2.Your HTML declares rel=canonical to /shoes, and a script later rewrites it to /shoes?color=red. What is the right fix?
  3. 3.A client-rendered SPA shows a "Product not found" view with HTTP 200 for deleted products. What happens and what should you do?
  4. 4.Your docs render fine in Search Console's URL Inspection but never get cited by ChatGPT or Claude. Content loads via a client-side fetch. What is the most likely technical cause?

Sources

Primary documentation this guide was checked against, last reviewed 5 October 2026.

  1. Understand the JavaScript SEO basics · Google Search Central
  2. Latest Google Search documentation updates (JavaScript entries, Dec 2025 to Mar 2026) · Google Search Central
  3. Googlebot (file size limits) · Google Search Central
  4. Robots meta tag, data-nosnippet and X-Robots-Tag specifications · Google Search Central
  5. The rise of the AI crawler · Vercel

Spotted something wrong or outdated? Report itHow we review

Finished reading?

Mark it complete to track your progress. Saved in this browser, no account needed.