Fixes · Page speed and images

Hero image stays blank until the whole page has loaded

The fix

Many slideshows hide the first slide until their script starts, and fall back to starting on the window load event, which waits for every image and app script on the page. A lazy-image script can also hide a first image that has already downloaded.

Draw the first slide with no script, start the slider on DOMContentLoaded (deferred scripts have run by then), and give the first image loading="eager" and fetchpriority="high".

01Why the banner waits for the whole page

The usual advice, on threads like this one (Aug 2023), is to stop lazy-loading the first slide, and that matters (web.dev, lazy-loaded LCP images). On both stores we fixed, the first image already loaded eagerly. It downloaded, then waited for a script.

  1. 01

    The slider waits for load

    Slider set-up code often says “start now if the library is ready, otherwise on window load”. If that code sits inline in the section and the library is loaded with defer, the inline code always runs first, finds no library, and waits for load. MDN: the load event fires “when the whole page has loaded, including all dependent resources such as stylesheets, scripts (including async, deferred, and module scripts), iframes, and images, except those that are loaded lazily” (MDN, load event). On a store with several apps, that is close to the last thing to happen.

  2. 02

    The first slide is hidden until the slider starts

    Themes often keep the slideshow at opacity: 0 until it is set up, to avoid a jump. Put that together with the above and the banner stays blank for the whole page load.

  3. 03

    A lazy-image script holds the first image back

    Some themes hide each image while their script marks it as loading, then fade it in once it has loaded. On the first slide, that ties the banner to the script being ready, the image’s load event, and a fade.

02Find what the banner is waiting for

For store owners: is your banner waiting for a script?

  1. Load the homepage on a slow connection

    In Chrome on a computer, open the developer tools (View > Developer > Developer Tools on a Mac, ⋮ > More tools > Developer tools on Windows), choose Fast 4G in the Network tab, and reload. Watch whether the banner appears with the first things on the page, or near the end.

  2. Switch JavaScript off and reload

    With the developer tools open, press Cmd+Shift+P (Ctrl+Shift+P on Windows), type Disable JavaScript, choose it and reload. If the banner image shows straight away now, the theme’s script is what holds it back. Send your developer what you saw, and switch JavaScript back on the same way.

For developers: paint first, start the slider after

This is the shape of what we shipped, generalised:

The first slide paints with no script (Liquid and CSS)liquid
{%- style -%}
  /* Before the slider starts: show the first slide, hide the rest. */
  #Slideshow-{{ section.id }} .swiper:not(.swiper-initialized) .swiper-slide { width: 100%; flex-shrink: 0; }
  #Slideshow-{{ section.id }} .swiper:not(.swiper-initialized) .swiper-slide + .swiper-slide { display: none; }
{%- endstyle -%}

<div id="Slideshow-{{ section.id }}">
  <div class="swiper">
    <div class="swiper-wrapper">
      {%- for block in section.blocks -%}
        {%- liquid
          assign loading = 'lazy'
          assign priority = 'auto'
          if forloop.first and section.index == 1
            assign loading = 'eager'
            assign priority = 'high'
          endif
        -%}
        {%- if block.settings.image != blank -%}
          <div class="swiper-slide">
            {{ block.settings.image | image_url: width: 2000 | image_tag:
              loading: loading,
              fetchpriority: priority,
              sizes: '100vw',
              widths: '750, 1100, 1500, 2000, 2400' }}
          </div>
        {%- endif -%}
      {%- endfor -%}
    </div>
  </div>
</div>
Start the slider when deferred scripts have run, not on loadjs
function startSlider() {
  // new Swiper(...) or your slider's set-up
}

// Deferred scripts run before DOMContentLoaded, so the library is ready by then.
if (typeof Swiper !== 'undefined') {
  startSlider();
} else {
  document.addEventListener('DOMContentLoaded', startSlider);
}
  • MDN: DOMContentLoaded fires “when the HTML document has been completely parsed, and all deferred scripts ... have downloaded and executed” (MDN, DOMContentLoaded). It “doesn’t wait for ... async scripts”, so load the slider library with defer, not async.
  • Only the first slide of the first section is eager and high priority (section.index is 1-based, and is nil in the theme editor, in static sections and through the Section Rendering API, so there the check falls back to lazy: measure on the preview link, not in the editor). The other slides stay lazy.
  • fetchpriority="high" on the main image makes it load sooner (web.dev, Fetch Priority).
  • For the first slide, image_tag’s preload: true also starts the download before the browser reaches the image: Shopify sends it as a Link header with rel=preload, and the <img> itself doesn’t change (Shopify, image_tag). Set it on that one image only; Shopify says to use it “sparingly”.
  • No fade and no loading class on the first slide. If the theme has a lazy-image script, leave the first image out of it.
  • A desktop and a phone image: use one <picture> with a <source media> for the phone image, so each device downloads one. Two eager <img> tags with one hidden by CSS both download.
  • Look for the same load fallback in other sections. Theme code is often copied from one section to the next.

03Check with JavaScript off, then on

  1. JavaScript off

    The first slide shows, and the other slides don’t stack under it.

  2. JavaScript on, Fast 4G

    The banner appears with the first content on the page, and the slider then starts without a jump.

  3. Find the main element

    In the Performance panel, record a reload. The Largest Contentful Paint should be the first slide’s image, and come early.

  4. Count the images

    In the Network tab, filter by Img on a phone-sized window and on a desktop one: one banner image each, not both.

From our work

04Two homepages, two different holds

A beauty brand’s new theme (11 Aug 2026). The hero slideshow was invisible until its slider started. The slider’s set-up, inline in the section, ran before the deferred slider library, so it always fell back to the window load event, behind every image and app script on the page. The first slide now paints as a still frame with no script, the slider starts on DOMContentLoaded, and the first image has fetchpriority="high". The same load fallback was in eight other sections and snippets, and all of them moved over. The desktop and phone banners became one <picture>, so phones download only their own. We didn’t record a before and after timing on that store.

Thameen London (20 Oct 2026). On Thameen London’s homepage, the first slide was already eager and high priority. What held it back was the theme’s lazy-image script: once it ran, it hid the image while it loaded, waited for its load event and a quiet moment in the browser, then faded it in over half a second. The first slide of the top slideshow is now a plain image outside that script; the later slides stay lazy. This came out of a call with the Thameen team the same day.

Thameen London’s homepage on a phone with JavaScript switched off: the header, then the first slide of the banner, a photo of two blue fragrance bottles with its heading and Shop Now button, and the top of the next section below
Thameen London’s live homepage at phone size with JavaScript switched off, 23 Oct 2026: the first slide shows with no script.
  • In our live log“Homepage banner's first image appears as soon as it has loaded”
  • In our log“Homepage banner shows its first slide straight away instead of after the whole page loads”

The bigger picture

05The first screen, loaded last

The top of the homepage is the first thing every visitor sees, and a script that waits for the whole page puts it last. If your store feels slow and the banner is one of the last things to arrive, find out what it is waiting for before you compress another image.

06Sources

Every step and claim above comes from one of these, or from the fix on the store it describes.

  1. MDN: Window, load event
  2. MDN: Document, DOMContentLoaded event
  3. MDN: The script element (defer)
  4. Shopify developers: Liquid objects, section (index)
  5. Shopify developers: Liquid filters, image_tag (preload)
  6. web.dev: Optimizing resource loading with the Fetch Priority API
  7. web.dev: The performance effects of too much lazy loading
  8. Shopify Community: Prevent lazyloading slideshow images, Sense theme (Aug 2023)
  9. Shopify Community: Why does my slideshow load slowly? (May 2022)

Sources checked on .

Contact

First screen slow to appear?

Send us your homepage address, and tell us what seems to load last.