0

I'm new to craft cms and I'm trying to generate srcset sizes for an image with the following code.

{% set thumb320 = { width: 320, format: 'jpg' } %}
{% set thumb320_2x = { width: 640, format: 'jpg' } %}
{% set thumb360 = { width: 320, format: 'jpg' } %}
{% set thumb360_2x = { width: 640, format: 'jpg' } %}
{% set thumb412 = { width: 414, format: 'jpg' } %}
{% set thumb412_2x = { width: 828, format: 'jpg' } %}
{% set thumb600 = { width: 600, format: 'jpg' } %}
{% set thumb600_2x = { width: 1200, format: 'jpg' } %}
{% set thumb768 = { width: 603, format: 'jpg' } %}
{% set thumb768_2x = { width: 1206, format: 'jpg' } %}
{% set thumb1024 = { width: 803, format: 'jpg' } %}
{% set thumb1024_2x = { width: 1606, format: 'jpg' } %}
{% set thumb1280 = { width: 1003, format: 'jpg' } %}
{% set thumb1280_2x = { width: 2006, format: 'jpg' } %}
{% set thumb1440 = { width: 1120, format: 'jpg' } %}
{% set thumb1440_2x = { width: 2240, format: 'jpg' } %}
{% set thumb1600 = { width: 1224, format: 'jpg' } %}
{% set thumb1600_2x = { width: 2448, format: 'jpg' } %}
{% set thumb1920 = { width: 1244, format: 'jpg' } %}
{% set thumb1920_2x = { width: 2488, format: 'jpg' } %}
{% set thumb2560 = { width: 1368, format: 'jpg' } %}

<picture>
    <source media='(min-width: 2560px)' srcset="{{ image.getUrl(thumb2560) }} 1x">
    <source media='(min-width: 1920px)' srcset="{{ image.getUrl(thumb1920) }} 1x, {{ image.getUrl(thumb1920_2x) }} 1.5x">
    <source media='(min-width: 1600px)' srcset="{{ image.getUrl(thumb1600) }} 1x, {{ image.getUrl(thumb1600_2x) }} 1.5x">
    <source media='(min-width: 1440px)' srcset="{{ image.getUrl(thumb1440) }} 1x, {{ image.getUrl(thumb1440_2x) }} 2x">
    <source media='(min-width: 1280px)' srcset="{{ image.getUrl(thumb1280) }} 1x, {{ image.getUrl(thumb1280_2x) }} 2x">
    <source media='(min-width: 1024px)' srcset="{{ image.getUrl(thumb1024) }} 1x, {{ image.getUrl(thumb1024_2x) }} 2x">
    <source media='(min-width: 768px)' srcset="{{ image.getUrl(thumb768) }} 1x, {{ image.getUrl(thumb768_2x) }} 2x">
    <source media='(min-width: 600px)' srcset="{{ image.getUrl(thumb600) }} 1x, {{ image.getUrl(thumb600_2x) }} 2x">
    <source media='(min-width: 412px)' srcset="{{ image.getUrl(thumb412) }} 1x, {{ image.getUrl(thumb412_2x) }} 2x">
    <source media='(min-width: 360px)' srcset="{{ image.getUrl(thumb360) }} 1x, {{ image.getUrl(thumb360_2x) }} 2x">
    <img itemprop="image" src="{{ image.getUrl(thumb320) }}" alt='{{ image.title }}'>
</picture>

The problem is that this is generating a lot of queries and I wanted to find out if there is a more efficient way of coding this?

1 Answer 1

2

You can use the method Asset::getSrcset to generate a valid srcset attribute as well as the asset transforms included in it. But you will need to restructure your HTML a bit. The current approach works, but is very verbose and not optimal for different screen resolutions and image placements.

Instead of using multiple source, just use one source without a media attribute that includes all variants in its srcset. Then add a sizes attribute that will tell the browser how large the image will be displayed depending on the viewport width. If the image always spans the full page, you can just set your sizes attribute to 100vw (100 viewport width units). If your image is sized differently depending on the viewport, you will need to adjust the sizes attribute to match those sizes. Here's a great article on MDN on responsive images, including how to use the sizes attribute.

Now the final code can look like this:

<picture>
    <source {{ attr({
        type: 'image/jpeg',
        srcset: image.getSrcset(
            [320, 360, 412, 600, 768, 1024, 1280, 1440, 1600, 1920, 2560],
            { format: 'jpg', }
        ),
        sizes: '100vw',
    }) }}>
    <img width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.alt }}">
</picture>

The great thing about this approach is that browsers will take care of adjusting for pixel density for you. If you open the page with this image on a smartphone with a 360px wide display (CSS pixels) and a 2x display density (so a 720px screen), the browser will automatically select the 768px image, since it's the smallest available size that's large enough for the screen's resolution and density.

Note that as per the spec browsers may select images based on other factors, such as using a smaller images if network conditions are poor, or using a cached image even if it's smaller than the currently required resolution. But in practice this rarely matters.

By the way, with the code above, you can very easily add a second <source> with the same srcset and sizes attributes but using type image/webp and format: 'webp' in the transform to generate WebP variants of the images for browsers that support it. This will have a large impact on web performance.

5
  • Perfect thank you a lot for helping me. I take the opportunity to ask you how precise you are in determining the various image sizes and how many you usually generate (for how many resolutions)?
    – Samuel
    Feb 22 at 18:38
  • I have noticed that by using imagerx there are significant performance improvements (a lot less database queries). Do you use such a plugin for responsive images?
    – Samuel
    Feb 22 at 19:01
  • 1
    @Samuel Most of our projects have containers with fixed widths (per viewport) and a multi-column layout (using Bootstrap, for example), so we have ~6 different fixed sizes in pixels – though it depends on the image positioning. For an image that stretches the whole viewport, there's only a single size. Then we generate the srcset with those sizes as well as some others for different pixel densities. You can't get every size in every possible resolution, that would be hundreds of images, so we usually just 'fill in the gaps' between sizes so that most devices will find a suitable image.
    – MoritzLost
    Feb 23 at 8:59
  • 1
    @Samuel We don't use a plugin, the built-in image resizer works just fine for us. The resizing is a one-time operation for each placed image, after all, and the HTML output can be cached, avoiding excessive queries for most requests. BTW we have a reusable twig template to take care of this stuff, every other template just includes that template and passes it an image, the sizes attribute and the list of sizes for the srcset. I've written a bit about that here: craftcms.stackexchange.com/a/38383/11705
    – MoritzLost
    Feb 23 at 9:02
  • 1
    Great! thank you so much for your valuable answers
    – Samuel
    Feb 23 at 14:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.