In May 2024, we shipped AVIF support. All images on Framer are now served as AVIF, which makes them ~20% smaller. However, integrating this format was challenging, partly because converting images to AVIF is slow. Here’s how we solved this.
Challenge: AVIF Encoding Is Slow
At Framer, we optimize every image on the first request. The optimized image is then cached on a CDN.
This is a common approach, and it works well, but it comes with a drawback. Because the first uncached request has to convert and resize the image, it takes longer than subsequent ones. With WebP, “longer” is noticeable but acceptable: in our infrastructure, WebP conversion typically adds 100-300 milliseconds. However, with AVIF, this grows to 1-2 seconds.
Side note : “1-2 seconds? Isn’t that still fast?” — It’s fast, but only outside computer contexts. Research shows that users start feeling things aren’t instant after just 100 milliseconds.
Framer’s cache hit for images is ~98%. If we ignored this issue and switched to AVIF, every 50th image would take several seconds to load. We felt this was unacceptable, so Framer’s Jacob came up with, and Piotr shipped a clever strategy that avoided that – the stale-while-revalidate header.
Solution: Stale-While-Revalidate
stale-while-revalidate is a caching setting. It’s a parameter in the Cache-Control header, and it tells CDNs how long they can keep serving the image after it expires:
This is a common approach, and it works well, but it comes with a drawback. Because the first uncached request has to convert and resize the image, it takes longer than subsequent ones. With WebP, “longer” is noticeable but acceptable: in our infrastructure, WebP conversion typically adds 100-300 milliseconds. However, with AVIF, this grows to 1-2 seconds.
Cache-Control: max-age=3600, stale-while-revalidate=60 ↑ how long a file can be cached for ↑ how long a CDN can keep serving the file after max-age expires
Here’s how we used it to make sure AVIF never makes image responses slow:
- First Request: WebP
- On the first request, we serve the image as WebP, not as AVIF.
- We also set the Cache-Control header to max-age=0, stale-while-revalidate=31536000
- Immediate Expiry
- Because max-age is set to 0, the WebP image expires immediately. This prompts the CDN to forward the second request to us.
- Second Request: AVIF
- When the second request arrives, we serve the image as AVIF.
- Responding to the second request can take several seconds because AVIF conversion is slow. But thanks to stale-while-revalidate, our CDN (CloudFront) keeps serving the WebP image until the conversion is complete.
- We recognize the second request from the first by the If-None-Match header. Only the second request has it.
- When the AVIF image is ready, we return it with Cache-Control: max-age=31536000. This allows the CDN to cache and serve it for a long time.
- When the second request arrives, we serve the image as AVIF.

When We Don’t Use AVIF
AVIF is now the default format for most images. But there are still a few scenarios where we continue to use WebP:
- Lossless Images: AVIF’s lossless compression is not truly lossless and also worse than WebP’s. So, for lossless images, we keep using WebP.
- Animated Images: The library we rely on for image optimization doesn’t support animated AVIF images, so we continue using WebP for these.