← Back to blog

Technical SEO · July 18, 2026 · 7 min read

Lazy Loading Done Wrong: When It Hides Content from Googlebot

Lazy loading SEO mistakes — intersection observer misuse, infinite scroll, and DOM-injected content — can silently block Googlebot from indexing your pages.

By FluxWriter Team

Lazy Loading Done Wrong: When It Hides Content from Googlebot

Lazy loading SEO problems rarely announce themselves — your Lighthouse score stays green, your pages feel fast, and yet Googlebot quietly fails to index half your content. The intersection observer API and infinite scroll patterns are the usual suspects, and the failure modes are specific enough to miss entirely if you're only running surface-level audits.

Why Googlebot Isn't Your Browser

Before getting into patterns, you need one mental model locked in: Googlebot renders pages with a crawl budget and a render queue. It executes JavaScript, but not on your timeline. Google's own documentation acknowledges that JavaScript rendering can be deferred by seconds, days, or indefinitely depending on crawl queue pressure. When lazy loading depends on scroll events or intersection thresholds that fire only after a human-like interaction, content that never gets triggered never gets indexed.

The practical implication: anything hidden behind a scroll trigger that Googlebot doesn't reach is effectively invisible to organic search.

The Four Lazy Loading Patterns That Break Indexing

1. Intersection Observer with No Fallback

The IntersectionObserver API is the modern, performant replacement for scroll listeners. The problem is that it was designed for browser viewports, not headless renderers. Googlebot's Chrome-based renderer does support IntersectionObserver, but it uses a simulated viewport — typically 1080px wide and varying in height — and it doesn't simulate scrolling unless your JavaScript explicitly triggers it.

A typical broken pattern:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadContent(entry.target);
    }
  });
});

document.querySelectorAll('.lazy-section').forEach(el => observer.observe(el));

If .lazy-section elements sit below the simulated fold, isIntersecting never fires, loadContent() never runs, and the text, links, and structured data inside those sections are never present in the rendered DOM when Googlebot snapshots the page.

Fix: Either server-side render the critical content or use a rootMargin large enough that elements intersect within the initial viewport. Setting rootMargin: "0px 0px 200px 0px" pulls the trigger threshold up by 200px, but a safer approach is rendering above-fold content unconditionally and only lazy-loading true below-fold assets (images, embeds) rather than text content.

2. Infinite Scroll Without Pagination Fallback

Infinite scroll is the most indexing-hostile pattern in common use. The content loaded via scroll is typically fetched from an API endpoint after the initial page render. Googlebot sees the first batch of results and stops there.

A 2023 study by Zyppy (analyzing 1,500+ e-commerce category pages) found that pages relying solely on infinite scroll had, on average, 47% fewer product URLs indexed compared to paginated equivalents. The pattern effectively caps your crawl at whatever fits in the initial server response.

The Google Search Central documentation recommends one of two approaches for paginated content:

Approach Indexability Implementation Cost
Static pagination (?page=2) Full Low
<link rel="next/prev"> (deprecated but tolerated) Partial Low
Infinite scroll + History API pushState Partial (unreliable) High
Infinite scroll only, no fallback Near zero Low (and the problem)

The only reliable fix is maintaining a <noscript>-compatible or server-rendered paginated alternative that Googlebot can traverse link by link.

3. Content Loaded via data-src Without a JS Fallback

This one affects body text, not just images. Some CMS plugins lazy-load entire content blocks by storing the actual HTML in a data-content attribute and writing it into the DOM via JavaScript on scroll. The rendered HTML Googlebot receives before scroll simulation shows an empty container.

An example of the broken pattern:

<div class="lazy-block" data-content="<p>Our service covers 42 metros...</p>"></div>
// Fires only on scroll
el.innerHTML = el.dataset.content;

Googlebot won't see "Our service covers 42 metros" in its rendered snapshot. That text, and any keywords it carries, are gone from the index.

4. Tabs, Accordions, and "Show More" Collapsed Behind Click Events

Tabs and accordions are not the same as lazy loading, but they share the same failure mode when the hidden content is removed from the DOM entirely rather than visually hidden with CSS. display: none content is generally indexed by Google (they've confirmed this repeatedly since 2016). Content that is only injected into the DOM after a click is not.

The distinction matters:

/* Indexed — content is in the DOM */
.tab-panel { display: none; }
.tab-panel.active { display: block; }
// NOT indexed — content added to DOM on click
tabButton.addEventListener('click', () => {
  tabPanel.innerHTML = fetchedContent; // Content never in initial DOM
});

If your FAQ answers, product descriptions, or specification tables live inside dynamically injected tab panels, they are not contributing to your rankings.

How to Audit for These Issues

URL Inspection in Search Console

The single most reliable tool is Google Search Console's URL Inspection > "View Tested Page" > "More Info > HTML." This shows the rendered HTML as Googlebot saw it, not your server-side HTML. Scroll through it and look for empty containers where content should be.

Mobile-Friendly Test (Rendered HTML)

The Mobile-Friendly Test shows a screenshot of how Google rendered your page. If sections appear blank or cut off, the screenshot itself tells you what Googlebot saw.

Comparing Cached vs. Live Content

Run a fetch of your page with curl -A "Googlebot" <your-url> and compare the raw HTML against your browser's "View Source." If sections that exist in the browser source are absent from the curl output, those sections are dynamically loaded and Googlebot may not render them. If they're present in both but still missing from the URL Inspection tool, the JavaScript is the failure point.

Screaming Frog + JavaScript Rendering

In Screaming Frog, crawl first with "JavaScript Rendering: None" and then again with "JavaScript Rendering: Chrome." Compare the word counts and link counts between the two crawls. Large discrepancies (more than 15% fewer words in the non-JS crawl) point at lazy-loading content that Googlebot may miss.

The Right Way to Lazy Load

Lazy loading has legitimate performance benefits. The goal isn't to eliminate it — it's to restrict it to assets that don't carry indexable content.

Lazy load these safely:

Never lazy load these:

For text-heavy content that you want to load progressively for performance, server-side rendering (SSR) or static generation is the right answer. The page can hydrate and enhance client-side, but the content should be present in the initial HTML response.

FAQ

Does Google's renderer support IntersectionObserver?

Yes, the Chrome-based WRS (Web Rendering Service) used by Googlebot does support IntersectionObserver. The issue isn't API support — it's that Googlebot doesn't scroll, so elements below the simulated viewport never trigger intersection callbacks. Code that depends on user-like scrolling to fire will fail to execute.

If I use display: none, will Google still index the hidden content?

Generally yes. Google has stated multiple times that CSS-hidden content is indexed, though it may be given slightly lower weight than prominently visible content. The SEO risk is meaningfully lower than content injected into the DOM after a user interaction. The critical distinction is whether the content exists in the rendered DOM, not whether it's visually visible.

My page scores 90+ on Lighthouse but some content isn't indexed. How?

Lighthouse measures performance, accessibility, and best practices against browser rendering — it doesn't test for Googlebot-specific rendering gaps. A page can load instantly in a human browser and still have lazy-loaded content that Googlebot never triggers. URL Inspection in Search Console is the correct diagnostic tool for indexability, not Lighthouse.

Takeaway

Lazy loading SEO problems are invisible in standard monitoring until you check what Googlebot actually rendered. Run a URL Inspection diff against your live page source before assuming your JavaScript-heavy pages are fully indexed. The failure modes above — intersection observer misuse, infinite scroll with no fallback, and DOM-injected content — are each fixable once you know which pattern you're dealing with. If you're publishing technical content at scale and want the underlying rendering and crawlability handled without constant manual auditing, tools like FluxWriter are built with these constraints in mind from the start.



← All posts