Adding AVIF and WebP Sup­port to My Craft CMS Site

Five years ago, I wrote about AVIF: A New Image Format (back then). Since then, I’ve implemented WebP and AVIF support on numerous client sites for considerable performance improvements – but my own site was still serving JPEG, PNG, and GIF images only. So it was time to fix that. Here’s how I added modern image format support to my Craft CMS site.

Modern Image Formats Rock 🚀 #

But first, a little reminder, why using only JPEGs in 2025 means missing out on a lot of what the modern web offers in terms of performance:

WebP offers 25-34% smaller file sizes than JPEG at equivalent quality and is widely supported by all modern browsers. AVIF – which still many people don’t use or even don’t know about, it seems – provides even more impressive gains. Studies show it can deliver file sizes that are significantly smaller than JPEG (approximately 10 % better than WebP) while maintaining superior color depth and detail preservation. For users, this means even faster page loads, reduced data usage, and a better overall experience. For site owners, it means lower bandwidth costs and improved SEO rankings. There are just no downsides to adding those formats to your site today.

The beauty is that using them is a textbook example for progressive enhancement: if we use the <picture> element with multiple <source> tags, browsers that support AVIF or WebP load the optimized versions, while older browsers gracefully fall back to JPEGs.

Using Modern Formats in Craft 5 #

Craft CMS 5 makes it straightforward to generate and serve multiple image formats. First, make sure your Craft installation supports WebP and AVIF. Craft 5 includes this out of the box when using ImageMagick (or also GD), but you may need to check if your server has the necessary libraries installed. Then, there are actually two ways to implement WebP and AVIF.

Using Named Image Transforms #

The first way is using named image transforms. Image transforms in Craft are great. You basically predefine certain sizes and file formats, name them, and now, editors can use them in Craft’s Control Panel when creating new content.

If you want, you can create new image transforms and select AVIF or WebP as the format, respectively. But this isn’t actually necessary. Craft can override the format of a transform, even if the transform has a default format set (e.g. to JPEG).

To output an image with a named transform applied as AVIF or WebP in your front-end templates, you pass its handle into your asset’s getUrl() function and set the format. And you replace existing image tags with the <picture> element to serve multiple formats:

{% set image = entry.myImage.one() %}
{% if image %}

    {# AVIF first = most modern format, smallest size #}
    
    
    {# WebP as fallback for browsers that support it (>95%) #}
    
    
    {# JPG/PNG fallback #}
    {{ image.title }}

{% endif %}

Just by adding {format: 'avif‘} and {format: 'webp'}, Craft will generate the images needed. 🎉

Generating AVIF and WebP Directly in Your Templates #

But to add AVIF and WebP to your site, you actually don’t even need named image transforms. It works equally well if you just take any image and, for example, set a width and the format when using getUrl:

{% set image = entry.myImage.one() %}
{% if image %}
  
    
    
    {{ image.title }}
  
{% endif %}

Responsive Images #

The last step is to use responsive images, to make the whole thing even more performant. Here is a basic example:


  
  
  
  
  {{ image.title }}

Or, if you want to take it one step further, you can move the responsive image sizes into an array and iterate over it to generate the scrset strings. This is, for instance, how the template on my site looks like in the end (more or less):

{# Define responsive widths #}
{% set widths = [480, 768, 1024, 1536] %}
{% set sizes = "(min-width: 48em) calc(100vw - 340px), (min-width: 64em) min(915px, calc((100vw - 340px) * 9 / 14)), 100vw" %}

{# Build srcset strings #}
{% set avifSrcset = [] %}
{% set webpSrcset = [] %}
{% set fallbackSrcset = [] %}

{% for w in widths %}
    {% set avifSrcset = avifSrcset | merge([image.getUrl({ width: w, format: 'avif' }) ~ ' ' ~ w ~ 'w']) %}
    {% set webpSrcset = webpSrcset | merge([image.getUrl({ width: w, format: 'webp' }) ~ ' ' ~ w ~ 'w']) %}
    {% set fallbackSrcset = fallbackSrcset | merge([image.getUrl({ width: w }) ~ ' ' ~ w ~ 'w']) %}
{% endfor %}


    
    
    
    {{ image.alt }}

Your mileage may vary, but I actually like that I can quickly adjust the image widths and the sizes string at the top at one central position.

*** EDIT ***

Wait! Thanks to Harald Atteneder for pointing me on Mastodon to Craft’s getSrcset asset function! This is a function you can use to generate the scrset attribute automatically – and it will also generate the individual images for you. While searching for examples on how to best use the getScrset function, I stumbled upon a great article by Oliver Spies on how to generate AVIF images. Based on that input, I just rewrote the section above, which makes it much cleaner.

{% set srcsetSizes = ['480w', '768w', '1024w', '1536w'] %}
{% set sizes = block.wide ? "100vw" : "(min-width: 48em) calc(100vw - 340px), (min-width: 64em) min(915px, calc((100vw - 340px) * 9 / 14)), 100vw" %}


    {% if craft.app.images.getSupportsAvif() %}
        {% set avif = clone(image) %}
        {% do avif.setTransform({ format: 'avif', quality: 60 }) %}

        {{ tag('source', {
            srcset: avif.getSrcset( srcsetSizes ),
            sizes: sizes,
            type: "image/avif"
        }) }}
    {% endif %}
    {% if craft.app.images.getSupportsWebp() %}
        {% set webp = clone(image) %}
        {% do webp.setTransform({ format: 'webp', quality: 80 }) %}

        {{ tag('source', {
            srcset: webp.getSrcset( srcsetSizes ),
            sizes: sizes,
            type: "image/webp"
        }) }}
    {% endif %}
    {{ tag('img', {
        src: image.url,
        width: image.width,
        height: image.height,
        alt: image.alt,
        srcset: image.getSrcset( srcsetSizes ),
        sizes: sizes,
        loading: "lazy"
    }) }}

Besides using the getSrcset function with a set of widths – now with a w at the end: ['480w', '768w', '1024w', '1536w'] – you can see that I am now rendering the <source> tags with the Twig tag function. This allows me to set the scrset with the getSrcset function as well as to define a type, which was previously done within the getUrl function. Also, I am now checking for support for both modern formats with getSupportsAvif() and getSupportsWebp(). Only if the respective format is supported, the <source> tag will be rendered, which makes the template snippet even more flexible.

What About GIFs? #

There is one special case, where it gets interesting: on my site, GIFs should probably stay as GIFs because I am mostly using them animated. While you can convert static GIFs to WebP or AVIF for better compression, animated content is trickier.

WebP supports animation and offers much better compression than GIF. According to Google's research, animated GIFs converted to lossy WebP are 64% smaller, while lossless WebP versions are 19% smaller. However, AVIF animation support remains limited in browsers as of early 2025.

So, for now, I am just checking whether an image is a GIF and then converting it to WebP:

{% set isGif = image.extension == 'gif' or image.getExtension() == 'gif' %}

{% if not isGif %}
	{# … the  element from above … #}
{% else %}
	
    {# Animated WebP if available #}
    
    
    {# GIF fallback #}
    {{ image.title }}
{% endif %}

Keep in mind that Craft will only convert GIFs to animated WebP if your image driver supports it. Otherwise, you may need to handle animated GIFs separately or use video formats for longer animations, which often provide even better compression.

And that’s it – actually not that complicated at all and another great example of how straightforward it is to do such upgrades that deliver measurable performance improvements in modern PHP-based content management systems. I might write a similar post for Kirby CMS soon … 🤔 Your existing JPEG images can coexist with the new formats, and Craft handles the heavy lifting of generating the variants for you.

More About Modern Image Formats #

This is post 19 of Blogtober 2025.

~

50 Webmentions

  1. @matthiasott you should also take a look at the `getSrcset` asset function. https://docs.craftcms.com/api/v5/craft-elements-asset.html#method-getsrcset craft\elements\Asset | Craft 5 Class Reference
  2. Adding AVIF and WebP Support to My Craft CMS Site: matthiasott.com/notes/adding... "Matthias Ott is an independent user experience designer and web design engineer from Stuttgart, Germany. He also teaches Interface Prototyping at the Muthesius…"

13 Reposts

ⓘ Webmentions are a way to notify other websites when you link to them, and to receive notifications when others link to you. Learn more about Webmentions.