# How do you lazy-load below-the-fold sections in a Shopify theme?

Source: https://themestackguide.com/lazy-load-below-fold-shopify-theme-sections/

Most Shopify themes render every section on initial page load, including the testimonial carousel and Instagram grid nobody scrolls to. Deferring below-the-fold sections with an IntersectionObserver cuts initial render work and speeds up interactive time, as long as you never defer anything in the first viewport.

Open the network tab on a typical Shopify homepage and you will see the problem: dozens of images requested at once, JavaScript initializing carousels for sections far down the page, and the browser doing layout work for content the visitor may never see. Themes render sections eagerly because it is simpler, not because it is faster. Lazy-loading sections is one of the highest-leverage performance edits you can make to a theme, and it takes about thirty lines of JavaScript.

## Why sections are the problem

A Shopify homepage is a stack of sections: hero, featured collection, testimonials, brand story, Instagram feed, newsletter. Each section brings its own images, and many bring their own JavaScript. Browsers are good at prioritizing above-the-fold content, but only if you tell them what is below the fold. An `img` tag with no loading hint gets fetched eagerly by default, and a carousel script runs its setup code regardless of scroll position.

The cost shows up in two metrics. Total blocking time climbs because the main thread initializes components for offscreen sections. And data usage climbs because images download that nobody views, which matters on mobile connections. Both get better when sections below the fold wait their turn.

## The IntersectionObserver approach

The clean way to defer a section is to mark it in Liquid, then reveal it with an observer. In your section file, wrap the output so the section starts inert:

```
<div class="lazy-section" data-lazy-section>
  {{ section.settings.heading }}
  ...section content...
</div>
```

Then add one small script to your theme. It watches every marked section and adds a loaded class when the section approaches the viewport, which triggers your CSS and any per-section initialization:

```
document.addEventListener('DOMContentLoaded', () => {
  const sections = document.querySelectorAll('[data-lazy-section]');
  if (!('IntersectionObserver' in window) || sections.length === 0) {
    sections.forEach(s => s.classList.add('is-loaded'));
    return;
  }
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('is-loaded');
        observer.unobserve(entry.target);
      }
    });
  }, { rootMargin: '400px 0px' });
  sections.forEach(s => observer.observe(s));
});
```

The `rootMargin` of 400 pixels starts loading the section before it scrolls into view, so visitors never see a blank gap. The fallback at the top handles old browsers by loading everything immediately, which is the safe default. Put this in your theme's main JavaScript file so it runs on every template, and mark only the sections that sit below the fold on desktop and mobile.

## Handle images inside deferred sections

Deferring the section markup is half the job; the images inside need the same treatment. For images in lazy sections, use native lazy loading as the baseline:

```
{{ image | image_url: width: 1200 | image_tag: loading: 'lazy' }}
```

The `loading="lazy"` attribute tells the browser to wait until the image nears the viewport. Combined with the section observer, this means offscreen images cost nothing until they are needed. For responsive images, keep your `srcset` intact; lazy loading works with responsive markup, and the browser still picks the right size.

One subtlety: if your observer reveals a section and its images only start loading then, there can be a visible pop-in. The 400-pixel root margin mostly solves this, because image fetches begin before the section is visible. For image-heavy sections like lookbooks, bump the margin to 600 pixels.

## What you must never lazy-load

Do not defer anything in the first viewport. The hero image, the headline, and the primary call to action must render immediately, because they usually contain the Largest Contentful Paint element. Lazy-loading your LCP image is the classic way to make this optimization backfire: the page looks faster in your testing tools and slower in the only metric visitors feel.

A good rule: if a section is visible without scrolling on a 1366 by 768 laptop or a 390-pixel-wide phone, it loads eagerly. Everything else is a candidate. When in doubt, test with the observer on the section and watch LCP in a field tool before and after. If LCP moves, you deferred too much.

## Testing the change

Verify in two places. In dev tools, throttle to a mid-tier mobile connection and confirm that offscreen images no longer appear in the initial network waterfall. Then scroll and confirm each section reveals smoothly with no layout shift; if content jumps, the section needs a min-height placeholder so the page length stays stable while it loads. Run the page through a speed test before and after and compare total blocking time and fully-loaded bytes, not just the headline score.

## The verdict

Lazy-loading below-the-fold sections is a small, safe edit with outsized returns: less initial render work, fewer wasted image downloads, and a faster-feeling page. Mark offscreen sections, reveal them with an IntersectionObserver and a generous root margin, keep native lazy loading on the images inside, and never touch the first viewport. Test LCP before and after, and you have a performance win you can measure.

Will lazy-loading sections hurt SEO?
:   No, as long as the content is in the HTML and reveals on scroll. Search crawlers render JavaScript and see the full page. Do not lazy-load by removing content from the DOM or loading it via separate requests that crawlers might miss.

Is this better than just lazy-loading images?
:   Image lazy loading handles the bytes; section deferral also skips the JavaScript initialization and layout work for offscreen components like carousels and feeds. Do both.

Can apps break this?
:   Yes. Some apps inject their own sections or initialize on page load regardless of your markup. After implementing, scroll the full page and check that app widgets in deferred sections still initialize correctly.

## Frequently asked questions

### Should I edit my live theme or a copy?

Always work on an unpublished copy for anything beyond a tiny text change. Preview the copy, test the cart and checkout paths, then publish when it checks out. Your previous version is your rollback.

### What is a metafield in Shopify?

A metafield is a custom data field attached to a product, collection, or page, like a care instruction or a sizing note. Themes can display metafields natively, which makes them the clean way to store per product content.
