Deep Dive into Shopify Image Tag | No more blurry images


Welcome to the world of Shopify’s dynamic image rendering courtesy of the image_tag. Visual appeal is paramount, and Shopify’s image tag & filters provides a powerful way to more easily implement and optimize your theme images. In this blog, we’ll dive into the nuances of the Shopify image_tag filter, exploring how to strike the perfect balance between image quality and web page performance.

Watch on YouTube

If you prefer to watch this demonstration enjoy, otherwise read on.

Simple Image Tag in Liquid

Per the Shopify blog: “The simplest version of an image_tag creates an HTML tag, sets the width and height attributes to prevent layout shift, and sets the source to a resized version of our image with the given width. It also automatically serves the best image file format – usually WEBP or AVIF on modern browsers.”

Essentially it turns this: {{ product | image_url: width: 200 | image_tag }} into this:

<img src="//...light-new.jpg?v=1683744744&amp;width=200" srcset="//polinas-potent-potions.myshopify.com/cdn/shop/files/science-beakers-blue-light-new.jpg?v=1683744744&amp;width=200 200w" width="200" height="133">

So at it’s base, it creates an HTML image tag using the image_url (Shopify filter for a cdn image) and specifies it’s height and width. Why does this matter? Because setting height and widths on images is crucial to a better web experience. Users (and Google) do not like janky loading when images finally load.

Lazy Loading and Section Index

Any developer that has been tasked with optimizing a theme’s performance has undoubtedly come across “lazy loading”. This is a process of deferring the loading of images on your site until they are actually (close to) in view. Loading images is costly on performance, why load images at the bottom of the site when the user will have to scroll down there eventually?

Luckily the image_tag has made this simple for you. Do nothing! Really, really. The image tag auto inserts a loading=”lazy” attribute for sections lower down the page! We can customize this behavior using the {% section.index %} value. The section index returns an integer in order from 1+ of how high the section in question is on the page.

Shopify auto inserts the lazy loading attribute for anything lower than 3. I personally find that a bit to lenient, and like to customize the tag to lazyload anything lower than 1 or 2. You can accomplish this in liquid like so:

{%- liquid
  if section.index > 2
    assign loading = "lazy"
  else
    assign loading = "eager"
  endif
-%}

{{
  section.settings.image
  | image_url: width: 1080
  | image_tag: loading: loading
}}

Additional Filters like Crop

Another bonus with the image tag, is there are several other filters we can use for custom behavior. This means less CSS, hooray! One example is ‘Crop’. Let’s say you have this landscape sized image:

but you need a square image of 400×400 on your theme, but the actual image dimensions are 550x 400. Using ‘crop’ you can specify what you would like to focus on and voila.

{{ section.settings.image | image_url: width: 400, height: 400, crop: 'center' | image_tag }}

With no CSS necessary!

Adding ‘srcset’ via ‘widths’

Let me introduce you to DPR, or device pixel ratio. Developers know the size we want to display the image, but we don’t know the DPR of the user’s device. Enter the srcset attribute. The srcset provides a set of candidate files. The browser knows the user’s device screen size and DPR, so it can then pick the best image to download based on the candidate files we give it. In Shopify liquid, this: {{ product | image_url: width: 600 | image_tag: widths: '200, 300, 400' }} turns into:

<img src="//p...-light-new.jpg?v=1683744744&amp;width=600" srcset="//polinas-potent-potions.myshopify.com/cdn/shop/files/science-beakers-blue-light-new.jpg?v=1683744744&amp;width=200 200w, //polinas-potent-potions.myshopify.com/cdn/shop/files/science-beakers-blue-light-new.jpg?v=1683744744&amp;width=300 300w, //polinas-potent-potions.myshopify.com/cdn/shop/files/science-beakers-blue-light-new.jpg?v=1683744744&amp;width=400 400w" width="600" height="400">

I had worked a little with srcset before, and had thought this was about rendering a bigger image on a bigger screen. But its not. It’s also about rendering a higher pixel count. For example, you would think a desktop browser would use the largest image and a mobile phone would use the smallest. BUT, since mobile phones have such a high resolution these days they are actually downloading the largest image in most cases!

Again, the image_tag will do the heavy lifting for us by generously creating the srcset urls for the ‘widths’ we pass it. Note we still need to define the default width to our larger image size.

Now if you test this out, you will notice that the image is showing as the set width every time. How can I fix this? Shopify suggests adding a style of width size to the image tag. 

Compare the original image I used with only a defined width with this new tag loading the higher resolution image! (If you can’t tell from my screenshot, the latter is higher resolution).

Adding Sizes to Image Tag

Why does the sizes attribute matter? Because whenever we add a srcset to an image, the browser will assume that image is displayed at the 100% viewport width. Meaning it ONLY considers the screen size. The sizes attribute is a hint to the browser to tell it how large the image will actually be rendered. This leads to improved performance by telling the browser “hey on smaller screens, this image will take up 95% of the viewport width vs on large screens it will only take up 50%”.

We can add it like so:

{{ product | image_url: width: 200 | image_tag: sizes: '(min-width:1600px) 960px, (min-width: 750px) calc((100vw - 11.5rem) / 2), calc(100vw - 4rem)' }}

Conclusion

I hope you have enjoyed this blog about the Shopify liquid image_tag. This guide should give you more confidence in the implementation of images in your themes for improving performance, development speed, and user experience.