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:
- Plain
image_tag
- Adding
srcset
to animage_tag
naively - Fixing the display width of our image
- Adding the
sizes
attribute - Using
<picture>
for art direction - 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&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:
- Use CSS to resize the image.
- 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>