Skip to content

Add native lazy loading for images to the storefront

Add native lazy loading for images to the storefront

INFO

This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository. You can find the original version here

Context

  • Currently, the images/thumbnails inside the Storefront are not making use of any lazy loading mechanism.
  • Without a third-party extension which includes something like "lazysizes" it is not possible to get lazy loading images.
  • Native lazy loading of images is available in current browsers, see: https://caniuse.com/?search=loading

Decision

  • We want to make use of native lazy loading for images in the Storefront without adding additional JavaScript logic.

Consequences

  • We pass a new attribute loading="lazy" to several usages of the thumbnail component Resources/views/storefront/utilities/thumbnail.html.twig. This enables native lazy loading.
  • By default, the thumbnail component uses loading="eager" which is the default behaviour and has the same effect as not setting the attribute.
  • The default is not lazy in order to avoid unexpected behaviour for extensions which might have added the thumbnail component while using a JavaScript solution for lazy loading.
  • We add native lazy loading in appropriate areas to reduce the initial network load:
    • Main menu flyout: Category preview images will only load when flyout is being opened.
    • Product boxes: Product images will only load when they appear in the viewport inside the listing. This also affects product sliders with horizontal scrolling, e.g. cross-selling.
    • CMS image elements: CMS layouts will only load images which appear in the viewport (e.g., when scrolling down the page).
    • Line item images: Product images in line items (e.g., cart page) will only load when they appear in the viewport.

Why don't we just add loading="lazy" everywhere?

  • Even though this would technically work, there are a few pitfalls that need to be considered.
  • For example, it is not recommended to add lazy loading to images which are very likely inside the initial viewport when loading the page aka "above-the-fold". Further reading: https://web.dev/browser-level-lazy-loading-for-cmss/#avoid-lazy-loading-above-the-fold-elements
  • For a system like shopware, where the content is almost entirely dynamic, it is not easy to determine where a generic image component will be rendered. It could have any position on any CMS page.
  • Even "guesses" like "only add lazy loading after the 8th product in a listing" can be invalid as soon as a monitor is in portrait mode or the viewport changes to mobile.
  • Therefore, we live with the small drawback that, e.g., all product boxes have lazy loading. Some of them will appear "above-the-fold". However, we still have the benefit of loading images later when scrolling down a page or scrolling in product sliders.
  • Implementing a JavaScript solution for this would contradict the usage of native lazy loading.

Areas without loading="lazy"`

  • Image gallery on product detail page: This is very likely "above-the-fold" and the gallery already uses JavaScript lazy loading for the image zoom as well.
  • Image sliders (CMS): When sliding to the next image, the lazy loading can lead to a bad user experience because the image can appear too late.

How to activate lazy loading?

When using the thumbnail component, pass the loading attribute with value lazy:

diff
{% sw_thumbnails 'my-thumbnail' with {
    media: category.media,
    attributes: {
        'class': 'my-css-class'
+        'loading': 'lazy'
    }
} %}