Demo: Responsive images using Liquid

Liquid and the Shopify image CDN can help make image generation and markup easier. The image_tag filter (docs) in Liquid should be your go-to choice for rendering an HTML <img> tag. It generates all the markup necessary to make your image responsive. This demo starts from the perspective of delivering responsive images, and shows how the image_tag behaves with different attributes so that we can achieve our optimization goals.

Note that we always feed an image_url into the image_tag. The image_tag uses Shopify's image CDN API to manipulate your images to provide different sizes and formats. It can also perform other tasks like cropping. Learn more in the docs.

Jump to a section:

  1. Plain image_tag
  2. Adding srcset to an image_tag naively
  3. Fixing the display width of our image
  4. Adding the sizes attribute
  5. Using <picture> for art direction
  6. Fixing oversized mobile images

Plain image_tag Jump to heading

The simplest version of an image_tag creates an HTML <img> 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.

Liquid

{{ section.settings.image | image_url: width: 300 | image_tag }}

Rendered HTML

<img
src="//cdn.shopify.com/s/files/1/0657/6730/9530/files/dog.jpg?v=1664903481&amp;width=300"
width="300"
height="393">

Rendered image

Adding srcset to an image_tag naively Jump to heading

To achieve the equivalent of a custom srcset, we need to bump up the width for the image_url to our maximum natural image width and supply the widths of all candidates we want in the widths attribute for the image_tag. Note the difference in singular and plural of the word "width" as its important to get correct. While the image_tag can supply an automated srcset, the results may not be what you expect so I suggest supplying your own.

Without any CSS or other attributes, this image will display at the max natural width, which is not what we want...

Liquid

{{ section.settings.image |
image_url: width: 600 |
image_tag: widths: '300, 600' }}

Rendered HTML

<img
src="//cdn.shopify.../files/dog.jpg?width=600"
srcset="
//cdn.shopify.../files/dog.jpg?width=300 300w,
//cdn.shopify.../files/dog.jpg?width=600 600w"

width="600"
height="785">

Rendered image

Fixing the display width of our image Jump to heading

We saw how to add a srcset, but our image is displaying at 600px rather than 300px making our srcset moot. We want it to display at 300px with a srcset that offers 1x and 2x versions. We have 2 options:

  1. Use CSS to resize the image.
  2. Use the width and height attributes on the image_tag.

The problem with option 2 is that it requires you to manually figure out the corresponding height or aspect ratio if you want to prevent layout shifts. We got that for free when we did not put a width attribute on the image_tag. So to keep things more maintainable, only put a width attribute on the image_url.

Thus, we're going with option 1: CSS. There are an infinite number of ways to implement this with CSS. For simplicity in this demo, I'm going to use inline styles directly on the image, but on a production site I would use a class. Note that my CSS is already setting a max width of 100% and a height of auto to prevent layout shifts.

Liquid

{{ section.settings.image |
image_url: width: 600 |
image_tag: widths: '300, 600', style: 'width: 300px' }}

Rendered HTML

<img
src="//cdn.shopify..../files/dog.jpg?width=600"
srcset="
//cdn.shopify..../files/dog.jpg?width=300 300w,
//cdn.shopify..../files/dog.jpg?width=600 600w"

width="600"
height="785"
style="width: 300px">

Rendered image

Adding the sizes attribute Jump to heading

Whenever we add a srcset to an image, the browser will assume that image is displayed at the viewport width. To prevent that, we need to give the browser a sizes attribute. It looks similar to CSS as it's telling the browser which widths the image will use at different viewport sizes. Here, the image_tag works like HTML and we can add the sizes attribute directly.

Liquid

{{ section.settings.image |
image_url: width: 600 |
image_tag:
widths: '300, 600',
sizes: '(min-width: 400px) 298px, 78.75vw',
style: 'width: 300px' }}

Rendered HTML

<img
src="//cdn.shopify.../files/dog.jpg?width=600"
srcset="
//cdn.shopify.../files/dog.jpg?width=300 300w,
//cdn.shopify.../files/dog.jpg?width=600 600w"

width="600"
height="785"
sizes="(min-width: 400px) 298px, 78.75vw"
style="width: 300px">

Rendered image

Using <picture> for art direction Jump to heading

If we want to show a different image at different screen sizes, we can use the <picture> tag. First, create an image_tag for the default srcset and wrap it in an HTML <picture>. Then, create a <source> tag for each additional image/media query you want to provide. Order matters, and the first match will be used.

In this example, resize your screen back and forth at 800px wide to see different doggos.

Liquid

<picture>
<source
media="(max-width: 800px)"
srcset="
{{ section.settings.image2 | image_url: width: 300 }} 300w,
{{ section.settings.image2 | image_url: width: 600 }} 600w,
"
>

{{ section.settings.image |
image_url: width: 600 |
image_tag:
widths: '300, 600',
sizes: '(min-width: 400px) 298px, 78.75vw',
style: 'width: 300px' }}

</picture>

Rendered HTML

<picture>
<source media="(max-width: 800px)" srcset="
//cdn.shopify.../files/dog2.jpg?width=300 300w,
//cdn.shopify.../files/dog2.jpg?width=600 600w,
"
>

<img
src="//cdn.shopify.../files/dog.jpg?width=600"
srcset="
//cdn.shopify.../files/dog.jpg?width=300 300w,
//cdn.shopify.../files/dog.jpg?width=600 600w"

width="600"
height="785"
sizes="(min-width: 400px) 298px, 78.75vw"
style="width: 300px">

</picture>

Rendered image

Fixing oversized mobile images Jump to heading

This example is very similar to the previous one on art direction. to x-descriptors for my mobile srcset to force the browser to only consider DPR. We're also using the same image rather than 2 different ones. I've included sample CSS so that the example is complete.

Liquid

<style>
.mobile-size-fix {
width: 300px;
}
@media screen and (min-width: 1000px){
.mobile-size-fix {
width: 1000px;
}
}
</style>
<picture>
<source
media="(max-width: 1000px)"
srcset="
{{ section.settings.image | image_url: width: 300 }} 1x,
{{ section.settings.image | image_url: width: 600 }} 2x,
"
>

{{ section.settings.image |
image_url: width: 2000 |
image_tag:
widths: '1000, 2000',
sizes: '(min-width: 1000px) 760px, (min-width: 800px) calc(100vw - 380px), (min-width: 400px) 298px, 78.75vw',
class: 'mobile-size-fix' }}
</picture>

Rendered HTML

<picture>
<source
media="(max-width: 1000px)"
srcset="
//cdn.shopify.../files/dog.jpg?width=300 1x,
//cdn.shopify.../files/dog.jpg?width=600 2x,
"
>

<img
src="//cdn.shopify.../files/dog.jpg?width=2000"
srcset="
//cdn.shopify.../files/dog.jpg?width=1000 1000w,
//cdn.shopify.../files/dog.jpg?width=2000 2000w"

width="2000"
height="2617"
sizes="(min-width: 1000px) 760px, (min-width: 800px) calc(100vw - 380px), (min-width: 400px) 298px, 78.75vw"
class="mobile-size-fix">

</picture>

Rendered image