Adding AVIF and WebP Support to My Craft CMS Site Posted October 21, 2025 by Matthias Ott 50 Webmentions #AVIF #blogtober #Craft CMS #development #images #performance #webp 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 #} {% 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 %} {% endif %} Responsive Images # The last step is to use responsive images, to make the whole thing even more performant. Here is a basic example: 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 %} 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 #} {% 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 # Using Modern Image Formats: AVIF and WebP - Smashing Magazine WebP Compression Study - Google for Developers WebP FAQ - Google for Developers Image Format Comparison: JPEG vs PNG vs WebP vs AVIF - Photutorial ❦ This is post 19 of Blogtober 2025. ~ 50 Webmentions Harald Atteneder 21 October 2025 | 22:03 @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 Harald Atteneder 22 October 2025 | 07:26 @matthiasott oh nice, web mentions FTW! Alex Jones 22 October 2025 | 22:46 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 alcinnz 21 October 2025 | 22:43 Will Browar 👨🏻💻 21 October 2025 | 22:44 Joe Crawford 22 October 2025 | 10:12 SouprMatt 22 October 2025 | 10:12 Harald Atteneder 22 October 2025 | 10:13 Scott Jenson 22 October 2025 | 10:13 Daniel González 22 October 2025 | 10:41 opio ⏚ 22 October 2025 | 10:41 Tom Arild Jakobsen 22 October 2025 | 11:11 Joan León 22 October 2025 | 11:37 björn 22 October 2025 | 19:24 Craft CMS 22 October 2025 | 19:24 Craft CMS 22 October 2025 | 19:24 34 Likes dan 21 October 2025 | 19:48 CJ :Polymaths: 👨💻 21 October 2025 | 22:43 Matthias Andrasch 21 October 2025 | 22:43 Iain Bean 21 October 2025 | 22:44 Eric Portis 21 October 2025 | 22:44 Simon Cox :SEO: 21 October 2025 | 22:44 Will Browar 👨🏻💻 21 October 2025 | 22:44 Jordi Sánchez 21 October 2025 | 22:44 Veikko Eeva 21 October 2025 | 22:44 Kristen Grote, Webmaster 21 October 2025 | 22:44 Allan Kukral 21 October 2025 | 22:44 Nilesh Prajapati 21 October 2025 | 22:44 Simon Cox :SEO: 22 October 2025 | 00:12 Joe Crawford 22 October 2025 | 10:12 Alex Russell 22 October 2025 | 10:12 Eric Portis 22 October 2025 | 10:13 Harald Atteneder 22 October 2025 | 10:13 OpenGraph Tools 22 October 2025 | 10:13 Simone Martelli 22 October 2025 | 10:13 Aram Zucker-Scharff 22 October 2025 | 10:13 Daniel González 22 October 2025 | 10:41 Barcelona Code School 22 October 2025 | 10:41 Will Richardson 22 October 2025 | 11:32 Joan León 22 October 2025 | 11:37 Brandon Kelly 22 October 2025 | 19:24 PerfReviews 22 October 2025 | 19:24 Craft CMS 22 October 2025 | 19:24 Manuel Matuzović 22 October 2025 | 19:24 Craft CMS 22 October 2025 | 19:24 Cumulonimbus 22 October 2025 | 22:40 Jay Greentree 22 October 2025 | 22:40 Andreas Atteneder 22 October 2025 | 23:52 Torb 26 October 2025 | 11:08 Terry Upton 26 October 2025 | 20:39 ⓘ 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. Have you published a response to this? Send me a webmention by letting me know the URL. Ping! More Notes Ad Infinitum Lazy and Prompt Buckle Up At Machine Speed
Harald Atteneder 21 October 2025 | 22:03 @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
Alex Jones 22 October 2025 | 22:46 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…"