Photo of a blue sky with fluffy white/gray clouds in front in the evening.

AVIF Preview Image Example

A common feature of web image formats is to display something – anything – while a large image is being downloaded. Progressive rendering allows the user to see that an image is being loaded and potentially start understanding the contents of the image. However, AVIF, being a video format, does not come with this feature.

What About AVIF?

AVIF developers debated including something similar to what I describe below, however, they tabled the idea. Including a preview image in the header would add complexity with little gain. Adding full progressive rendering this late would require a large change to the code & specification.

Developed for low-bitrate situations, they expect images should load quickly. It’s not being properly promoted for its expected use case. I will add a more detailed article covering this in the future.

Preview Image Example

This example shows how an AVIF image can display a lower quality preview while the larger image is loading. CSS restrictions require the use of a parent <picture> tag. If anyone knows a way to fold this into a single <img> tag, preferably without JavaScript, please let me know.

It uses no JavaScript for the actual image or loading portion. The script is only to add a more appealing transition effect that occurs when the large image has completed downloading. Browsers without JS will still load and display the image, but the large image will pop in instantly overtop of the inferior quality version with no transition animation.

Other image formats (WebP/WebP2, HEIF, GIF, PNG, etc) can also use this technique, but probably wouldn’t be necessary for types that support progressive encoding/rendering (such as JPEG and JPEG XL) as they will provide a similar effect without the use of an extra image or code.

Lets take a look at the HTML structure…

<picture id="avif-cats" class="block" style="background-image: url('');">
    <img class="block" width="4032" height="3024" style="display:none;" src="" decoding="async">
    <img class="block" width="4032" height="3024" onload="this.imageIsLoaded();" src="" decoding="async">

Wait… why are there 2 img tags? Some browsers will try to load the large image first which can block loading of the preview image until it is done. Yikes! That is not what we would want. The first one is linking to the same preview image that is set as the background of the picture tag. Note the display is also none making it invisible. This is specifically so that it will prioritize it above the larger image when loading resources.

Take a look at the code. (It’s recommended you run the code after the DOM has finished loading [DOMContentLoaded event]):

// This part can be anywhere inside or outside your event listener.
HTMLImageElement.prototype.imageIsLoaded = function() {'';;

(() => {
    let afterLoaded = function() {
        let isLoaded = false;
        let picElemCol = document.getElementsByTagName('picture');
        let imgElemCol = false;
        for(let picElem of picElemCol) {
            imgElemCol = picElem.getElementsByTagName('img');
            for(let imgElem of imgElemCol) {
                isLoaded = imgElem.complete && imgElem.naturalHeight !== 0;
                if(!isLoaded) {
           = "blur(3px)";
           = 0;

    if(/complete|interactive|loaded/.test(document.readyState)) {
    } else {
        document.addEventListener('DOMContentLoaded', afterLoaded, {"once": true});

The script is only used to blur the preview image until the large image has loaded, providing a more pleasing and interlaced JPEG-like appearance. Feel free to add a CSS animation to it, if you like.


Leave a Reply

Your email address will not be published. Required fields are marked *