← Knowledge Notes
Global Growth / Knowledge note · English

Complete JavaScript SEO Guide (Basics + Diagnosis + Dynamic Rendering + Lazy Loading)

Google's 3-stage JavaScript processing pipeline (crawl / render / index) + render queue mechanism + WRS (Web Rendering Service). 15 best practices + an 11-step diagnosis checklist. Common SPA (Single-Page Application) pitfalls: soft 404 errors / fragment routing / Local Storage failures / cache fingerprinting / traps of injecting robots meta via JS / shadow DOM in Web Components. Dynamic rendering is a legacy workaround; SSR / static rendering / hydration are recommended instead. Use viewport detection for lazy loading + convert infinite scroll to paginated URLs. Web Components must use Slot.

Source collection:Google Search and SEO · Published here:2026-09-26 · Note updated:2026-05-17

SEOSearch EnginesOrganic growth

Google Search is the only major search engine that runs JavaScript by default (using evergreen Chromium). Other search engines (Bing has partial support, DuckDuckGo / Yandex / most niche engines)do not run JS — SSR (Server-Side Rendering) remains the safest option.

Core mechanism Google uses to process JavaScript sites:WRS (Web Rendering Service, Web Page Rendering Service) + render queue.


1. Google's 3-stage JavaScript processing + render queue

URL
 ↓
[crawl queue 抓取队列] → Googlebot 取 URL → 检查 robots.txt
 ↓
fetch HTML → parse 解析 → 抽取链接回 crawl queue
 ↓
[render queue 渲染队列] → WRS(无头 Chromium)跑 JS → 渲染后 HTML
 ↓
parse 解析渲染后的 HTML → 抽链接回 crawl queue + 用于 indexing 索引
 ↓
[index 索引]

Key invariants:

  • All HTTP 200 pages enter the render queue,regardless of whether they contain JS(as determined by Google itself)
  • Non-200 status codes (e.g. 404) may skip rendering
  • Rendering may wait from a few seconds to much longer — depending on Google's available resources
  • WRS uses the current evergreen Chromium, kept in sync with the latest Chrome
  • Resources blocked by robots.txt → will not be fetched → missing resources for rendering → Google may not see the content

Counterintuitive: the render queue is asynchronous. If you change the page via hash changes but the render queue is congested, Google may see a snapshot from several days ago.


2. 15 JavaScript SEO Best Practices (Must Read)

Ordered by importance.

2.1 Content-related

# Practice Key points
1 Still use SSR (Server-Side Rendering) / prerendering Faster for both users and crawlers,non-Google crawlers can also see content
2 Unique, descriptive <title> + meta description Setting them via JS is acceptable, but ensure both exist when rendering completes
3 Use <a href> instead of onclick Google only extracts links from <a href> elements
4 Use the History API instead of URL fragments #/products Fragment-based routing like this is unreliable for Googlebot (the AJAX crawl scheme was deprecated in 2015)
5 Add structured data using JSON-LD; injecting via JS is also acceptable See Complete Structured Data Overview (Frameworks + Quick Reference for 34 Rich Result Types + JSON-LD/Microdata/RDFa Selection Guide)
6 For Web Components, use <slot><slot> to display light DOM Otherwise light DOM content will not appear in the rendered HTML → will not be indexed
7 Use viewport detection for lazy loading, not user scroll events Google Google does not scroll your page

2.2 Error handling

# Practice Key points
8 Use accurate HTTP status codes 404 for not found / 401 for login walls / 301 for moved content
9 SPAs must avoid soft 404 errors Client-side routing often returns 200 for not-found pages → fake 200 responses get indexed. Instead, use JS to redirect to a real 404 URL, or inject <meta name="robots" content="noindex">
10 Be careful when injecting robots meta tags via JS If Google sees noindex it may skip rendering → JS attempts to remove noindex are unreliable;if you want the page indexed, do not include noindex in the original HTML

2.3 Performance / Caching

# Practice Key points
11 Long-lived caching + content fingerprinting WRS may ignore cache headers; use main.2bb85551.js hash-based filenames to avoid issues
12 Feature detection + polyfills Detect unsupported APIs and provide fallbacks; for some features (e.g. WebGL) that Google does not support, render that portion via SSR
13 Use HTTP, do not rely on WebSockets / WebRTC Googlebot Only HTTP requests are sent, other connection types cannot be used

2.4 Canonicalization / Robots

# Practice Key points
14 rel="canonical" Place canonical tags in HTML first If you must inject via JS it is acceptable, but only one canonical can be retained; do not use JS to change the canonical to a different value than the original HTML
15 Do not force permission requests for Camera / Geo / Notification etc. Googlebot rejects permission requests; provide a fallback that works without permissions path

3. 6 Major SPA Pitfalls

Pitfall Symptom Fix
Soft 404 Client-side routing returns 200 for not-found pages → fake error pages get indexed Use JS to redirect to a real 404 URL, or inject <meta robots="noindex">
Fragment routing(#/products) Googlebot cannot crawl content after the fragment History API:window.history.pushState({}, '', '/products')
Local Storage / Session Storage failures Data lost across page loads WRS WRS clears Local / Session Storage and Cookies on every page load.Do not rely on persistent data to serve content
Fetching data via WebSocket / WebRTC Googlebot cannot retrieve the data Provide an HTTP fallback
Modifying canonical via JS Conflicting multiple canonicals → unpredictable behavior A single page can only have one canonical; check before injecting via JS
Fake JS paywall implementation Full content hidden in HTML + hidden via JS Actual access restriction must be done server-side. Hiding via JS is only cosmetic

4. 11-Step Diagnosis Checklist

When you suspect JavaScript is blocking indexing:

  1. Run Rich Results Test / URL Inspection Tool — check loaded resources / JS console / errors / post-render DOM. Use Search Console's Crawl Stats Report(Crawl Stats report) to monitor Googlebot + WRS activity
  2. Prevent soft 404s — see §3 item 1
  3. Expect Googlebot to reject permission requests — do not force Camera / Geo / Notification permissions
  4. Do not use URL fragments to load different content — use the History API
  5. Do not rely on data persistence — WRS clears Local / Session / Cookie on every load
  6. Use content fingerprinting — main.<hash>.js to prevent caching of stale resources
  7. Feature detection + fallbacks / polyfills for critical APIs
  8. HTTP connections are required — do not use WebSocket / WebRTC to fetch main content
  9. Ensure Web Components render correctly — <slot>Is <slot> used? Use the Rich Results Test to check whether the post-render HTML content is complete
  10. Check JS paywall implementation — full content should not be present in the HTML
  11. Test again after fixes — re-verify with Rich Results Test / URL Inspection. Pass with a green checkmark

Collect JS errors from production: It is recommended to set up a global onerror handler to log JS errors from both Googlebot and real users remotely, to identify causes of rendering failures.

window.addEventListener('error', function(e) {
    const errorText = [
        e.message,
        'URL: ' + e.filename,
        'Line: ' + e.lineno + ', Column: ' + e.colno,
        'Stack: ' + (e.error && e.error.stack || '(no stack trace)')
    ].join('\n');
    // POST 到自己服务器
    fetch('/logError', { method: 'POST', body: errorText });
});

5. Dynamic Rendering Is a Legacy Workaround (Not Recommended as of 2026)

Conclusion: do not use dynamic rendering. It has been downgraded from recommended status; use SSR / static rendering / hydration instead.

5.1 What it does

请求 → 检测 user-agent
  ├─ Googlebot / 不支持 JS 的抓取器 → 路由到 rendering server 渲染服务器 → 返回静态 HTML
  └─ 普通浏览器 → 返回 client-rendered 客户端渲染版本

5.2 Why it is deprecated

  • Increases server complexity (dual-track serving)
  • High maintenance cost
  • SSR / hydration solve the same problem and are simpler
  • The real "crawler list" is hard to maintain; new bots appear that you have not adapted to

5.3 Conditions under which it does not count as cloaking

As long as dynamic rendering serves similar content to both sides (server-rendered version ≈ client-rendered version), Googlebot does not treat it as cloaking.

Genuine cloaking(violating Google SEO Basics + How Search Works + Search Essentials §7 spam policies) example: showing cats to users, showing dogs to crawlers.

5.4 Modern alternatives

Solution Use case
SSR (Server-Side Rendering) Recommended by default. Next.js / Nuxt / Astro etc.
Static rendering Content that does not change often → generate HTML at build time
Hydration SSR + subsequent client-side takeover of interactivity
Prerendering (SSR-like) Convenient for small sites

6. Correct Lazy Loading Practices

6.1 Core principle

Lazy loading must use viewport detection, not scroll event triggers. Google does not scroll your page.

6.2 3 implementation methods

Method When to use
Native browser lazy loading loading="lazy"(<img> / <iframe>) Natively supported in modern browsers, simplest option
IntersectionObserver API + polyfill Custom logic, video lazy loading
JS libraries (react-lazyload etc.) Framework integration

6.3 What to avoid

  • Do not apply lazy loading to above-the-fold visible content (slow and pointless)
  • Do not rely on scroll event triggers (Google does not scroll)

6.4 Correct implementation of Infinite scroll

Infinite scroll is inherently at odds with SEO; it must be paginated:

6.5 Verification

In Search Console, go to URL Inspection Tool → Live URL → View the rendered HTML:

  • <img src> / <video src> All real URLs are present → Success
  • All are placeholder URLs → Failure; Google did not see the actual images

7. Key Quick Reference Cheat Sheet

What you want to do How to do it Warnings
Set page title Setting via JS is OK Make sure the title exists when rendering completes
Add structured data Inject JSON-LD via JS See Complete Structured Data Overview (Framework + One-Click Lookup for 34 Rich Result Types + JSON-LD/Microdata/RDFa Selection Guide)
Set canonical Prefer HTML; JS injection is OK Only one per page, do not create conflicts
Set robots meta Prefer HTML noindex Rendering may be skipped → cannot be changed via JS
Routing (SPA) History API Do not URL fragment(#/foo)
Handle 404 Redirect via JS to the real 404 URL, or inject noindex Client-side fake 200 responses will be indexed
Add image lazy loading Viewport detection Do not trigger on scroll
Implement infinite scroll Paginated URLs + History API updates Each chunk must have its own independent persistent URL
Detect API support Feature detection + polyfill Google does not support WebGL / WebSocket, etc.
Fetch data HTTP fetch Do not pull main content via WebSocket / WebRTC
Cross-page persistence Server-side WRS clears Local Storage / Session Storage / Cookies on every run
Add cache control Content fingerprinting WRS may ignore cache headers
Web Components <slot>Use <slot> to display light DOM Shadow DOM only content is not indexed
Paywall Control full content on the server side Hiding via JS does not count as access restriction

8. Relationship to Other Wiki Pages

If you want to know Go to
Overall Crawling Mechanism / Googlebot Details Complete Guide to Googlebot Crawling Mechanism and Indexing Control (URL/Links/Mobile-first/Meta/Testing/AMP)
Full canonical usage Complete Guide to Canonical / Redirects / Site Migration (Including Full HTTP Status Code Table + 503 Temporary Outage Playbook)
robots.txt + robots meta Complete Guide to Crawling and Indexing Control (robots.txt / noindex / X-Robots-Tag / data-nosnippet / rel)
33 types of structured data Complete Structured Data Overview (Framework + One-Click Lookup for 34 Rich Result Types + JSON-LD/Microdata/RDFa Selection Guide)
Full pagination practices Complete Ecommerce SEO Guide (6 Surface / 4 Launch Strategies / Merchant Center / 6 SD Types / 3 Pagination Modes / 13 Review Guidelines)
Search Console monitoring How to Use the Search Console + Analytics + Trends Trio (Traffic Diagnostics / Keyword Research / Looker Studio)
SEO Landscape / How Search Works Google SEO Starter Guide + How Search Works + Search Essentials

Sources and Related Materials