How do I use AVIF images in React Single page Web App

3.7k Views Asked by At

Final Update

For most practical purposes, this question is obsolete as both firefox and chrome have native support for avif through the standard picture html tag with a source marked as type="image/avif". See https://reachlightspeed.com/blog/using-the-new-high-performance-avif-image-format-on-the-web-today/ . Fire fox still likes to hang and often forces a control F5 to bypass caches and requires sending the correct content type from the server. Hopefully will be fixed soon.

Here is the commit where I got avif support working: https://github.com/quackack/quackack-comics/commit/f1a98ed1f40b6a22584d61bc338bd91df3232fa5#diff-e25b0950ce48f4e928f98e0a6fbb694c . Note that it contains many unrelated changes and in fact avif is barely mentioned, only as a content type and a file extension.

Original Question

I am trying to change a website where I host web comics from using jpegs to the newer avif image format. It is much smaller and seems to be the new image format with the most widespread support. Unfortunately, web browsers don't properly support the new format yet. So I was planning to use this package: https://github.com/Kagami/avif.js to allow my comics to be rendered. Some basic tests showed that AVIF would give the same quality as jpeg for less than half the space.

Unfortunately, after more than 5 hours of time spent on this, I am unable to get this to work with my react framework. You can see my website at the time of writing at 'https://github.com/quackack/quackack-comics/tree/cda4c3893d8477192c4ff3aa78d00096b7621ff7'.

I tried using npm install to install avif.js and then added

require("avif.js").register("/avif-sw.js");

to index.js . But I get error

Failed to register/update a ServiceWorker for scope ‘http://localhost:3000/’: Bad Content-Type of ‘text/html’ received for script ‘http://localhost:3000/avif-sw.js’. Must be a JavaScript MIME type

And avif files are still not able to load. I think the requests are getting rerouted to index.html instead of the javascript package. It seems like the appropriate thing to do is something like

import * as avif from 'avif.js';
avif.register('avif.js/avif-sw.js');

But this fails too with the same error, as do many other similar variations.

At this point I am inclined to wait for proper browser support for avif, as I don't get enough traffic to worry about data costs anyway. If this could easily be fixed, then I would love to have the improvements from avif. I just want smaller file sizes and widespread browser support.

Update

Okay, I found that I could get this to work if I changed from the default react bundler (which I believe to be webpack) to Parcel. Then it does work exactly as you expect... until I try to deploy the project.

There is an issue where I cannot load the service worker when i try to deploy my single page webapp to AWS as a single page web app. There it makes a request to my url with avif-sw.js where there is not actually a js file. I believe the issue is closely related to https://github.com/parcel-bundler/parcel/issues/670

So the first key is to use Parcel to build your web app. But Parcel still does something wrong with deployment it seems. I will continue to investigate this in a few days.

Here is the almost working version using parcel: https://github.com/quackack/quackack-comics/tree/parcel

Update 2

My earlier update was incorrect. I only thought it was working because of a cached service worker. My final solution is in the answer below.

2

There are 2 best solutions below

0
On BEST ANSWER

It doesn't seem to be a problem now. Simply convert your images to avif with tools like https://avif.io/ and use them as the image source or background source via typical CSS. As Chrome and Firefox now support it (even though users still have to enable it on Firefox), everything goes well. Even works on mobile now! :)

0
On

Okay, I finally got it working. Unfortunately, mobile devices don't seem to be able to handle the large file sizes so I had to keep using jpeg anyway. It worked on my laptop though.

Here is the commit that got everything to work: https://github.com/quackack/quackack-comics/commit/75e75307e688f0e515b4bbc9eb22eef290d2c209

What I had to do:

  1. Switch to Parcel.
  2. Copy the contents of the avif.js library into source before building. I used the command:

    copyfiles -f node_modules/avif.js/*.js .
    
  3. Put this specifically into reg.js:

    require("avif.js").register("./avif-sw.js");
    navigator.serviceWorker.register("./avif-sw.js", undefined);
    

What the last two steps do is trick parcel into actually keeping a copy of "avif-sw.js" around that can actually be loaded as a service worker. Probably with a bit more tinkering you can get this to work without using parcel at all, just by copying local and then registering. No requires required. But I stopped investigating after I found this solution can't work on mobile.

This was exceptionally hard to debug because service workers are cached by the browser and I had to clear broswer data after every edit. It was also hard to debug because the source files are cached to so I had to delete my projects cache and build frequently too.

You might also want to use npm module "http-server-spa", or similar, to test how your built SPA will act when deployed.