惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
Kaspersky official blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AI
AI
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Engineering at Meta
Engineering at Meta
博客园 - 叶小钗
The GitHub Blog
The GitHub Blog
Microsoft Azure Blog
Microsoft Azure Blog
N
News and Events Feed by Topic
Cloudbric
Cloudbric
B
Blog
Cisco Talos Blog
Cisco Talos Blog
V
Vulnerabilities – Threatpost
N
News and Events Feed by Topic
V
Visual Studio Blog
A
Arctic Wolf
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
U
Unit 42
S
Security @ Cisco Blogs
博客园 - 聂微东
T
Threat Research - Cisco Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Y
Y Combinator Blog
G
GRAHAM CLULEY
L
LINUX DO - 热门话题
量子位
NISL@THU
NISL@THU
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Troy Hunt's Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
月光博客
月光博客
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Hacker News
The Hacker News
Spread Privacy
Spread Privacy
D
Docker
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
博客园 - 司徒正美
T
The Exploit Database - CXSecurity.com
Hugging Face - Blog
Hugging Face - Blog
Help Net Security
Help Net Security
D
DataBreaches.Net

Matthias Ott

Hello Again, World This, Still Not for Everyone The Shape of Friction WeissKlang L1 – Punching Above Its Weight Continvoucly Morged Value Webspace Invaders To Affinity and Beyond The Mystery of Storytelling Amateurs! Echoes of Connection Linear() Is Not (That) Linear View Transitions: The Smooth Parts Challenge Acoustic Room Treatment and Building Sound Panels, Part 1: Planning Play On Overshoot The HTML Output Element Listening Closely Compressed Fluid Typography The Lifeblood of the Web What Could Go Wrong? That’s My Rank Making Space CSS :is() :where() the Magic Happens Visual Regression Testing for External URLs With Playwright Jane Goodall’s Famous Last Words European Tech Alternatives 🇪🇺 Independent Type Foundry Advent Calendar – Day 24: NaN Independent Type Foundry Advent Calendar – Day 23: Typotheque Independent Type Foundry Advent Calendar – Day 22: 205TF Independent Type Foundry Advent Calendar – Day 21: HvD Fonts Independent Type Foundry Advent Calendar – Day 20: Frere-Jones Type Independent Type Foundry Advent Calendar – Day 19: Fontwerk Independent Type Foundry Advent Calendar – Day 18: Vectro Independent Type Foundry Advent Calendar – Day 17: Studio René Bieder Independent Type Foundry Advent Calendar – Day 16: R-Typography Independent Type Foundry Advent Calendar – Day 15: David Jonathan Ross Independent Type Foundry Advent Calendar – Day 14: Interval Type Independent Type Foundry Advent Calendar – Day 13: Newglyph Independent Type Foundry Advent Calendar – Day 12: Swiss Typefaces Independent Type Foundry Advent Calendar – Day 11: Sharp Type Independent Type Foundry Advent Calendar – Day 10: Colophon Foundry Independent Type Foundry Advent Calendar – Day 9: Commercial Type Independent Type Foundry Advent Calendar – Day 8: Letters from Sweden Independent Type Foundry Advent Calendar – Day 7: Lineto Independent Type Foundry Advent Calendar – Day 6: Ohno Type Company Independent Type Foundry Advent Calendar – Day 5: Milieu Grotesque Independent Type Foundry Advent Calendar – Day 4: TypeMates Independent Type Foundry Advent Calendar – Day 3: Klim Type Foundry Independent Type Foundry Advent Calendar – Day 2: Dinamo Independent Type Foundry Advent Calendar – Day 1: Grilli Type The Independent Type Foundry Advent Calendar 2022 A Conversation With ChatGPT ChatGPT, please explain websites in the words of William Shakespeare Transient Frameworks Leaving Twitter Behind Converting Your Twitter Archive to Markdown The Wrong Question It Wasn’t Written Syndicating Posts from Your Personal Website to Twitter and Mastodon Suspension None of Your Business Shitty Code Prototypes Doing Our Part Patch That Package Brain Dump Generating Accessibility Test Results for a Whole Website With Evaluatory The CSS Cascade, a Deep Dive Updates About Updates How to Delete Your Commit History in Git Unblocking Your Writing Blocks, Part 2: I’m Not an Expert nor a “Thought Leader” Connections No Wrong Notes Better Options Design Debt Finite and Infinite Games Don’t Assume, Validate. Necessity Is the Ultimate Teacher One Egg Go Deep There Is No Secret Code Balancing Risk Blue Eyes, Brown Eyes The Shortcut Boomerang My RSS Feed Collection of Personal Websites Frequency The Illusion of Control The Decisions Journey Write It Down Nownownow Into the Personal-Website-Verse Considering the Opposite What is it for? Unlimited Bowling. Never done. We Are Team Internet. We Need to Save #NetNeutrality. Progressive Search Data loss (also) by JavaScript Books I Will Definitely Maybe Read in 2017 Starting to Write Notes
Adding AVIF and WebP Support to My Craft CMS Site
Matthias Ott · 2025-10-22 · via Matthias Ott

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.

~