Updated: 18 Sept 2023
Lazy loading is a performance strategy that de-prioritizes assets like images that are not needed immediately after a webpage is loaded. As with any strategy, you can misuse it causing worse performance.
In this post, I’ll cover what lazy loading is and how to use it effectively so that it improves your page loading speed.
What is lazy-loading? Jump to heading
In an ideal world, our webpage would only load the resources (files) it needs immediately before they are needed. Certain files are critical for initial load and rendering, like our HTML, critical CSS, and any images that will be visible in the viewport. Usually, we have many more resources that need to be loaded like offscreen images, additional CSS, and JavaScript.
Also, we may have iframes, or internal frames, on the page that will load many other resources. An iframe is a webpage embedded within our webpage, though usually it’s used for widgets like Youtube and Twitter embeds.
Lazy loading refers to the strategy of deferring the load of non-critical assets.
What strategies are available for lazy-loading images? Jump to heading
Many strategies exist for lazy loading in general, but here we will focus on images and iframes. We have two main strategies:
- Using a JavaScript library like lazysizes
- Using the native loading attribute
Native lazy loading is relatively new. Before, we used to recommend using lazysizes. However, now browser adoption of lazy loading for images is at 92% and 84% for iframes (as of August 2023). So we recommend using native lazy loading. By using a feature already embedded in browsers, we can keep our total code leaner and prevent technical debt.
How does it work? Jump to heading
By default, images are "eager" loaded. This means that as soon as the browser reads the HTML and comes across an <img>
tag, it will queue it up to download.
We can change that default behavior by setting the loading
attribute on an image to lazy
:
<img
src="cute_dog.jpg"
loading="lazy"
alt="Fluffy white dog playing with a ball"
/>
Now, when a browser reads this line in HTML, it will not download the image unless one of two things is true:
- The image in inside the viewport
- The user scrolls near to where the image will be
This is done with the help of the IntersectionObserver. In layman’s terms, the IntersectionObserver watches for when the view is near to “intersecting” with the image in the layout. It’s important to note that the IntersectionObserver requires JavaScript to be enabled. So if JavaScript is disabled, the image will never load.
Are there any gotchas? Jump to heading
The biggest gotcha with lazy-loading is that you do not want to lazy-load images that are in the viewport on load, or “above the fold”. You especially do not want to lazy-load an image that is used in your LCP (largest contentful paint) element.
When you set loading to lazy on an image that is inside the viewport, you’re adding a step that delays its load. This will make it render slower and feel slower to the end user, which is the opposite of our goal. So make sure that you either omit the loading attribute, or set it to “eager” instead of “lazy”.
To learn more about this antipattern, read How layout position impacts three big web performance levers.
How to implement lazy loading with Shopify Jump to heading
As with any website, if you’re writing the HTML directly, you can add the loading attribute directly into the <img>
tag, as shown in the example above.
However, we encourage you to use the Shopify Liquid image_tag
whenever possible because it comes with additional optimizations baked in. When using the image_tag
, by default it will set loading to lazy for images in sections further down the page. To take advantage of this default, do not set the loading attribute.
If you would like the override the default behavior, you can set the loading attribute as an additional HTML parameter:
{{ product |
image_url: width: 400 |
image_tag: class: 'custom-class', loading: 'lazy' }}
It's best to use this in conjuction with our new section.index
properties in Liquid:
{%- liquid
if section.index > 2
assign loading = "lazy"
else
assign loading = "eager"
endif
-%}
{{
section.settings.image | image_url: width: 1080 | image_tag: loading: loading
}}
To learn more about these new features and see more code examples, check out Announcing new Liquid features for better web performance.
Conclusion Jump to heading
Lazy loading is a key strategy to minimize the amount of bytes needed to load a page and to make the initial view render faster. In the case of images and iframes, browsers now allow us to set a loading
attribute in the HTML to let us control when we want to delay the load of an image file or all the files needed for an iframe.
Don’t forget that you do not want to lazy load anything above the fold. Lazy loading an image above the fold makes the page load appear slower to end users. This also increases your Largest Contentful Paint making your Core Web Vitals worse, which impacts your SEO as well.
If you’re developing themes in Shopify or other builders, consider using the default image_tag
loading behavior or using the section.index
property to set it based on logic that makes sense for your theme.