Twitter Behance Pinterest Instagram Mailchimp Dribbble

Responsive images with Statamic, Tailwind and Glide


Today I decided to include the new WebP image format in my responsive image solution I keep in my boilerplate. Happy with the result, I wanted to share this with other developers so you can improve your websites performance and get to page speeds up to a 100! This post is for Statamic v2 and assumes you’re an experienced Statamic developer. The whole concept will be the same for Statamic v3 and I’ll update this post as soon as I’m using v3 in production.

Configuring Statamic

We’re gonna configure Statamic so that it automatically generates asset presets when a user uploads an image to the control panel. We want Statamic to generate different sizes for each image so we can serve users the lowest image resolution possible depending on their device without sacrificing image quality. Statamic uses the Glide library for this. Luckily Glide can also convert images to WebP. So let’s throw that in their as well.

For all this magic you need to to edit your «sites/settings/assets.yaml» file. This is my current setup. Note that I use an environment variable for image_manipulation_cached. This is not default.

image_manipulation_route: /img
image_manipulation_secure: true
auto_crop: true
image_manipulation_driver: gd
image_manipulation_cached: {env:IMAGE_MANIPULATION_CACHED}
image_manipulation_presets:
  xs-webp:
    w: 320
    h: 9999
    q: 85
    fit: contain
    fm: webp
  xs:
    w: 320
    h: 9999
    q: 85
    fit: contain
  sm-webp:
    w: 480
    h: 9999
    q: 85
    fit: contain
    fm: webp
  sm:
    w: 480
    h: 9999
    q: 85
    fit: contain
  md-webp:
    w: 768
    h: 9999
    q: 85
    fit: contain
    fm: webp
  md:
    w: 768
    h: 9999
    q: 85
    fit: contain
  lg-webp:
    w: 1280
    h: 9999
    q: 85
    fit: contain
    fm: webp
  lg:
    w: 1280
    h: 9999
    q: 85
    fit: contain
  xl-webp:
    w: 1440
    h: 9999
    q: 85
    fit: contain
    fm: webp
  xl:
    w: 1440
    h: 9999
    q: 85
    fit: contain
thumbnail_max_width: 6000
thumbnail_max_height: 6000

Having this in place, make sure you add the following to your deploy script and run it if you already have some assets in your asset container.

php please assets:generate-presets

This will make Statamic generate all the different asset versions we want. Running this after deployment makes sure that the images are always there should the image cache be cleared.

If you use Redis for queue management users won’t notice any slowness during this process. I wrote a knowledge article about this if you don’t know how to set it up. It’s easy and worth the effort!

If you don’t do pre generate all the asset variants, Statamic will do this when the first visitor drops by. This will result in a terrible page load for this one unlucky person.

The Antlers <picture> partial

Now we’ve got our assets prepared, it’s time to make the picture.html partial.

<picture class="{{ class }}">
    {{ asset:image }}
        {{ if extension == 'svg' || extension == 'gif' }}
            <img src="{{ url }}" alt="{{ alt }}" />
        {{ else }}
            <source
                srcset="
                    {{ glide:url preset='xs-webp' }} 320w,
                    {{ glide:url preset='sm-webp' }} 480w,
                    {{ glide:url preset='md-webp' }} 768w,
                    {{ glide:url preset='lg-webp' }} 1280w,
                    {{ glide:url preset='xl-webp' }} 1440w"
                sizes="{{ sizes }}"
                type="image/webp"
            >
            <source
                srcset="
                    {{ glide:url preset='xs' }} 320w,
                    {{ glide:url preset='sm' }} 480w,
                    {{ glide:url preset='md' }} 768w,
                    {{ glide:url preset='lg' }} 1280w,
                    {{ glide:url preset='xl' }} 1440w"
                sizes="{{ sizes }}"
                {{ if extension == 'png' }}
                    type="image/png"
                {{ else }}
                    type="image/jpeg"
                {{ /if }}
            >
            <img
                {{ if cover }} 
                    class="w-full h-full object-cover" 
                    style="object-position: {{ focus | background_position }}"
                {{ else }}
                    class="w-full h-auto"
                {{ /if }}
                src="{{ glide:url preset='lg' }}"
                alt="{{ alt }}"
                loading="lazy"
            >
        {{ /if }}
    {{ /asset:image }}
</picture>

So what’s exactly happening here?

  1. We’re using a <picture> element because it can hold multiple <sources> for our images. For example if your browser doesn’t support WebP, it will fall back to JPG. I’m looking at you Safari.
  2. On the first line you’ll also notice we can define classes for our <picture> element. I’ll show an example of how to do this when we call in our partial later.
  3. On line 2 we’re using the {{ asset }} tag that uses an image as a variable. First on line 3 it checks to see if the asset is an .svg or .gif file. In that case just show the asset and we’re done. There’s nothing to optimise there. Just render the asset with an alt description and be done with it.
  4. On line 6 it get’s interesting. We’re defining a srcset for our image source with all the WebP presets we previously generated using our yaml configuration. We’re basically providing the browser a set of images and telling it at what width to use it.
  5. The value of the sizes attribute on line 13 is completely dependent on where and how you’re going to use this image. It expects something like this: (min-width: 768px) 40vw, 90vw”. What we’re telling the browser here is that the image is being rendered at 40vw on screens larger than 768px and 90vw on other (smaller) screens. Together with the srcset the browser can now figure out which of the 5 images it should download for the current screen our user is viewing the website on.
  6. At line 15 we’re repeating the same stuff for browsers that don’t support WebP and need JPG’s.
  7. On line 23 we define the correct image type.
  8. On line 29 some optional classes and a inline styles can be added when you’re using the object-fit css property. This utilises Statamic’s awesome focal point feature. That way we can object-fit: cover our image in a container and maintain focus on a user defined focal point. Here I use Tailwind utility classes.
  9. The default image on browsers that don’t support srcsets is the ‘lg‘ preset on line 33.
  10. And as a bonus: if your browser supports lazy loading that’s defined on line 35.

Calling in the partial

When we’re calling in the partial, just provide it with the necessary arguments. Take this example:

{{ partial src="picture" :image="image" class="w-full h-full object-cover" cover="true" sizes="(min-width: 768px) 40vw, 90vw" }}

And that’s it. We’re rendering a responsive image thats being held in our image variable. We’re passing the <picture> element some utility classes to define it’s appearance, set cover to true, and we’re letting the browser figure out which asset to download for 90vw on mobile and 40vw on larger screens. You could also wrap the partial tag in a <figure> element with a <figcaption>. It all depends on your use case.

I hope it’s helpful. Feel free to leave a comment if you have any questions or enhancements.

Gerelateerde artikelen:

CMS, Klanten, Statamic, Webdevelopment, WordPress

Statamic als alternatief voor WordPress


En wat kan het voor jou betekenen?