Technical SEO · Part 11

JavaScript SEO Basics: Rendering, Indexable Content & Lazy Loading

~16 min read Updated 2026-06-24 MagicSEO Editors · Human Reviewed Rendering & Indexing

Modern websites increasingly rely on JavaScript to render content. The question: Can search engines "see" this JS-generated content? The answer is "Yes, but with costs, delays, and uncertainty." Understanding Google's rendering mechanism, avoiding common client-side rendering pitfalls, and ensuring critical content remains stably indexable are essential technical SEO fundamentals for anyone building with frameworks.

Google's Two-Phase Rendering#

Google processes webpages in two stages (often called "two waves of indexing"). This mechanism was briefly covered in Crawling & Indexing; here's the detail:

  1. Phase 1: Crawling & Initial HTML Parsing. Googlebot crawls the URL, parses the returned raw HTML, extracts text and links, and queues the page for rendering. This step is near real-time.
  2. Phase 2: Rendering. Google's Web Rendering Service (WRS) executes the page's JavaScript using a headless Chromium browser to generate the final DOM. Only after this step does JS-injected content enter the index.
Rendering delay is the core riskA time gap exists between the two phases. Phase 1 completes in real-time, but rendering waits for WRS resources with delays ranging from hours to weeks. Content only appearing in Phase 2 (after JS execution) will have its discovery and indexing held up by this delay—while content in the initial HTML faces no such problem.

Three Rendering Modes#

For any given page, when and where content is "rendered" into HTML determines its SEO-friendliness.

ModeWhere Content is GeneratedInitial HTMLSEO Friendliness
CSR Client-Side RenderingUser browser executes JSShell (only container)⚠️ Rendering-dependent, delay risk
SSR Server-Side RenderingServer generates per requestFull content✅ Friendly
SSG Static Site GenerationPre-generated at build timeFull content✅ Most friendly (plus speed)

CSR: Client-Side Rendering

Pure CSR pages often have initial HTML as just an empty shell:

Pure CSR Initial HTML
<body>
  <div id="root"></div>   <!-- Empty, content injected via JS -->
  <script src="/app.js"></script>
</body>

Search engines in the first wave see only an empty <div>, waiting until the rendering phase completes app.js execution to see actual content. This is the root of SEO difficulties for Single Page Applications (SPAs).

SSR: Server-Side Rendering

SSR executes framework code on the server for each request, returning complete HTML with content already filled. The browser then "hydrates" to take over interactivity. Search engines see full content in the first wave. The tradeoff is servers need to render per request, with runtime overhead.

SSG: Static Site Generation

SSG renders all pages to complete HTML files at build time. After deployment, they're static files requiring no server runtime. It combines SEO-friendliness (content in HTML), extreme speed (CDN-ready), low costs, and small attack surface—ideal for content-focused sites.

How to ChooseContent-focused, infrequent updates (blogs, docs, marketing sites) → Prioritize SSG. Highly dynamic, user/request-specific content (dashboards, personalized pages) → SSR. Pure interaction apps with no SEO needs (admin tools) → CSR is fine. Many frameworks (Next.js, Nuxt, Astro) support mixing these modes per page.

Making Content Indexable#

Regardless of framework, there's only one core principle: Critical content should exist in initial HTML, or be reliably renderable. Specifically:

  • Don't rely solely on JS injection for critical text: Titles, body content, product info should ideally be server-rendered.
  • Meta tags belong in HTML: title, description, canonical, hreflang, structured data—best in initial HTML. JS dynamic injection works but adds uncertainty.
  • Don't differentiate by User-Agent: Serving different content to search engines vs users is cloaking, a violation of guidelines.
  • Avoid interaction-dependent rendering: Content requiring clicks or scrolling may be invisible to crawlers.

Links are the primary way search engines discover pages. The most common JavaScript site error is using script events to simulate "links," blocking crawlers from following.

Link Correct vs Incorrect Patterns
<!-- Good: Real a tag with href, crawler can follow -->
<a href="/articles/en/technical-seo/javascript-seo.html">JS SEO</a>

<!-- Bad: onclick navigation, no href, crawler can't discover target -->
<span onclick="location.href='/page'">Click</span>

<!-- Bad: Button as navigation -->
<button onclick="goTo('/page')">Next</button>

The rule is simple: Navigation must use real <a> tags with href pointing to real URLs. Front-end routing (like React Router) should also be based on <a href>, letting JS intercept clicks for client-side transitions while preserving crawlable hrefs. This aligns with URL structure and mobile navigation requirements.

Lazy Loading Best Practices#

Lazy loading significantly improves load speed, but improper implementation hides content from crawlers. The key distinction is "loading trigger method":

ApproachSEO Impact
Images with native loading="lazy"✅ Safe, browsers and crawlers handle correctly
IntersectionObserver lazy loading with real links/DOM content✅ Generally safe
User scroll required for next batch (no pagination links)⚠️ Crawlers don't scroll, may not see
"Load More" button without crawlable URL⚠️ Later content hard to discover
Infinite scroll needs real paginationGooglebot doesn't scroll like humans to trigger loading. If using infinite scroll for lists, provide crawlable pagination URLs (e.g., ?page=2 with real links per page) so crawlers can discover content sequentially. This is covered in detail in Pagination, Facets & Parameter URLs.

Common Pitfalls#

  • robots.txt blocking JS/CSS: Rendering needs these resources; blocking prevents proper rendering (see robots.txt configuration).
  • Critical content depends on client requests: Content filled only after JS API requests—if rendering fails or times out, there's nothing.
  • Using # fragments for routing: example.com/#/page style hash routing isn't index-friendly; use History API real paths instead.
  • meta/canonical overwritten incorrectly by JS: Dynamic injection bugs can write identical or wrong canonicals across all pages.
  • JS errors break rendering: One uncaught exception can prevent entire page content generation.
  • Rendering timeout: Overweight scripts or slow requests can cause rendering interruption before completion, leaving content missing.

How to Troubleshoot#

To determine "what search engines actually see," use these methods:

  • GSC URL Inspection Tool: View Google's rendered HTML and screenshot to confirm JS content visibility—the most authoritative method.
  • Rich Results Test / Mobile-Friendly Test: Also show Google's rendered results.
  • Compare rendered source: Browser "View Page Source" shows initial HTML; DevTools Elements panel shows rendered DOM. Comparing reveals JS-injected content.
  • Disable JavaScript: Turn off JS in browser and load page—what remains is the initial HTML portion.

For more details, see Google Official Docs: JavaScript SEO Basics.

Why This Site Uses Pure Static#

This site as demonstrationMagicSEO deliberately uses pure static HTML (essentially the most thorough SSG): every article's full text, titles, links, meta tags, and structured data are written into initial HTML, relying on no client-side framework rendering. Googlebot gets complete content in the first crawl wave, entirely skipping render queues and delays. The only JS on-page (theme switching, light motion) is progressive enhancement—even with JavaScript disabled, all content and navigation links remain fully functional. This is the ultimate practice of "critical content in initial HTML" advocated in this article.

JavaScript SEO Checklist#

  • Critical content visible in initial HTML (SSR/SSG or static)
  • title/description/canonical/structured data in HTML
  • Navigation uses real a tags with href
  • Front-end routing uses History API real paths, not # hashes
  • robots.txt doesn't block rendering-required JS/CSS
  • Images use native loading="lazy"
  • Infinite scroll paired with crawlable pagination URLs
  • Critical content doesn't require user click/scroll to appear
  • No different content served to search engines vs users (no cloaking)
  • GSC URL Inspection confirms complete rendered content

Frequently Asked Questions#

Can Google crawl JavaScript-generated content?

Yes, but with caveats. Google uses headless Chromium to execute JavaScript for rendering pages, so client-side rendered content is theoretically indexable. However, rendering requires queuing and consumes resources, with delays ranging from hours to weeks. Not every search engine or every crawl will render completely. Content that requires JavaScript to be displayed will have its discovery and indexing delayed with inherent uncertainty. The safest approach is to ensure core content exists in the initial HTML.

Are Single Page Applications (SPAs) bad for SEO?

Pure client-side rendered SPAs have inherent SEO disadvantages: the initial HTML often contains only an empty container, with content entirely injected via JavaScript. Search engines need additional rendering to see the content, with associated delays and uncertainties. The solution is not to abandon SPAs, but to add server-side rendering (SSR) or pre-rendering/static generation (SSG), so above-the-fold critical content appears directly in HTML while retaining client-side interactivity. Frameworks like Next.js and Nuxt are designed for this.

What's the difference between SSR, SSG, and CSR?

CSR (Client-Side Rendering) generates content in the browser via JavaScript, with initial HTML as an empty shell; SSR (Server-Side Rendering) generates complete HTML for each request on the server before sending to the browser; SSG (Static Site Generation) generates complete HTML for all pages at build time, deployed as static files. From an SEO perspective, SSG and SSR both make content visible in initial HTML, most friendly to search engines; SSG also offers fastest speed and lowest server costs, ideal for content-focused sites.

Does lazy loading affect SEO?

Properly implemented lazy loading improves SEO by enhancing page load speed. However, pure JavaScript scroll-based lazy loading (requiring user scroll or interaction to load content) won't be triggered by Googlebot, which doesn't scroll like humans. Best practices: use native loading="lazy" for images; ensure list content has crawlable real links or is accessible in HTML, and don't hide critical content behind user interactions.