AVIF is accepted by 94.67% of tracked browser usage, according to caniuse. The 2024 Web Almanac media chapter found it on 1.0% of mobile image requests. That is a 94 point gap between what browsers accept and what sites actually send, and it is not a browser problem. It is a pipeline problem: the upload path, the markup that selects a candidate, and the moment the browser finds out the image exists.
This guide works through those three layers in WordPress specifically: what core already does since 6.3, 6.5 and 6.7, what you still have to wire yourself, and which of the popular 2026 talking points do not survive a check against a primary source.
1. AVIF in WordPress core, and what core deliberately leaves out
WordPress 6.5 added AVIF support in February 2024. You can upload and serve .avif the same way you serve JPEG, with no plugin, provided the server’s Imagick or LibGD build can encode it. That condition is the first thing to check, and it has a page in the admin: Tools > Site Health > Info > Media Handling. Hosts vary widely here, and a theme that assumes AVIF on a box without it silently produces nothing.
What core does not do is convert anything on its own. An uploaded JPEG stays a JPEG, and its sub-sizes stay JPEG. The hook that changes this is image_editor_output_format, which has existed since WordPress 5.8 and maps a source mime type to an output mime type:
add_filter( 'image_editor_output_format', function ( $formats ) {
$formats['image/jpeg'] = 'image/avif';
return $formats;
} );Three consequences worth knowing before you ship that filter:
- The original upload is preserved untouched. Only the generated sub-sizes take the new format, which is what keeps the media library re-editable.
- It applies to future uploads only. Existing media needs a regeneration pass.
- Its default value is not empty. As of WordPress 6.7 core ships HEIC and HEIF mappings to JPEG in this filter, so return the array you were given rather than a fresh one.
The other core setting that shapes every upload is big_image_size_threshold, 2560 pixels since WordPress 5.3. Anything wider or taller is scaled down and the scaled copy becomes the largest available size. PNG uploads are excluded from that scaling. If your hero slot is genuinely wider than 2560, you are already serving an upscale and no amount of format work will fix the softness.
The cost nobody quotes
AVIF is not free at encode time. Cloudflare’s own transformation documentation states plainly that AVIF encoding can be an order of magnitude slower than encoding to other formats, and that if an image is too large to encode quickly, Cloudflare falls back to WebP or JPEG. The same arithmetic applies on your origin. A bulk regeneration of a 40,000 item media library to AVIF is a scheduled job with a queue, not a click in a settings screen.
Quality is also a global, per mime type setting rather than a per image judgment. WP_Image_Editor::$default_quality is 82, deprecated since WordPress 5.8.1 in favour of get_default_quality(), which returns 86 for WebP and that same 82 for JPEG and everything else. JPEG then passes through the jpeg_quality filter, which runs after wp_editor_set_quality and therefore has the last word. One number for the whole library is a compromise by construction: it is too high for flat illustrations and too low for skin tones and gradients.
2. JPEG XL: the high-fidelity alternative that you still cannot ship
This is where most 2026 guides, including the earlier version of this one, get it wrong. JPEG XL has not arrived.
caniuse puts JPEG XL at 15.02% global support, and that figure is generous because it counts partial support. The actual state, browser by browser:
| Browser | JPEG XL status |
|---|---|
| Safari (macOS, iOS) | Partial support, version 17 and later |
| Chrome, Edge | Disabled by default. Chromium enabled it, removed it, then reintroduced it as disabled-by-default in Chrome 145 |
| Firefox | Disabled by default |
| Android Chrome, Samsung Internet | No support |
The Web Almanac did not even measure JPEG XL usage in 2024, for the stated reason that Chromium-based browsers do not support the format. That is a more honest signal than any adoption chart: the data set that measures the whole web did not consider it a delivery format.
What this means in practice:
- As an archival or master format it is genuinely good. Lossless JPEG transcoding back and forth is a real feature and a real reason to keep JXL masters. That is a storage decision, not a delivery decision, and it never touches your
srcset. - If you do serve it, it has to sit as the first
<source type="image/jxl">in a<picture>with an AVIF source and a JPEG fallback behind it. Roughly 85% of your visitors will take one of the fallbacks, so the JXL branch buys you nothing you did not already have to build. - Do not use JPEG XL as the argument for delaying AVIF. AVIF is the format with 94.67% support today.
3. The LCP strategy: what WordPress 6.3 and 6.7 already decided for you
The advice to add loading="eager" and fetchpriority="high" by hand is three years out of date for content images. WordPress 6.3 added wp_get_loading_optimization_attributes(), which computes both attributes for images that pass through core’s rendering path. Its documented behaviour:
- The first qualifying image gets
fetchpriority="high". Qualifying means a minimum area of 50,000 pixels (width multiplied by height), adjustable through thewp_min_priority_img_pixelsfilter. loading="lazy"is omitted for images near the top of the document. The threshold filter iswp_omit_loading_attr_threshold, whose default rose from 1 to 3 in 6.3.- The function never emits
fetchpriority="high"andloading="lazy"together, because they are contradictory instructions.
WordPress 6.7 then added sizes="auto" for lazy-loaded images, via wp_img_tag_add_auto_sizes(). This is the fix for the oldest srcset bug in WordPress: the generated sizes attribute assumed the image filled the viewport, so a 300 pixel sidebar thumbnail could pull a 1024 pixel candidate on mobile. Because a lazy image does not load until layout is known, the browser can now use the real rendered width. Core prepends auto only when the image already carries loading="lazy", and enqueues a small CSS rule (wp_enqueue_img_auto_sizes_contain_css_fix()) to avoid a layout side effect.
So if core covers the common case, what is left to do? Two failure modes:
- The LCP element is not an
imgtag core rendered. A CSS background image, avideoposter, a page builder that writes markup outsidewp_filter_content_tags(). Core cannot prioritise what it cannot see, so the hero arrives after the stylesheet that references it. - The LCP element differs by breakpoint. A desktop hero photo and a mobile card image are different elements. A static guess is right for one of them.
Both are what Image Prioritizer exists for. It depends on Optimization Detective, which collects URL Metrics from real visitors per breakpoint and therefore knows which element actually was the LCP rather than guessing from document order. With that data it emits a fetchpriority=high preload for the LCP image URL, as both an HTML LINK element and an HTTP Link header, applies the attribute only at breakpoints where the element really is the LCP, sets fetchpriority=low on images that are inside the viewport but hidden (carousel slides two and three, the classic case), and lazy-loads CSS background images including those declared in stylesheets from allowed origins.
4. Content-aware compression: what is real and what is marketing
The claim that an encoder detects a face, compresses the sky aggressively and delivers a 70% saving is the kind of number that gets repeated without a source. Treat any such figure as unverified until you have measured it on your own library.
What is real and checkable:
- Per-image quality beats a global quality number. The two quality filters take different arguments, and mixing them up is how a per format rule ends up applying to everything.
wp_editor_set_qualityreceives the default quality, the mime type and the image dimensions, so it can vary quality by format and by size.jpeg_qualityreceives only the quality and a context string such asimage_resize. A photo-heavy portfolio and a UI screenshot library do not want the same setting. - AVIF degrades differently from JPEG. JPEG fails visibly, with blocking and ringing that you can spot in a thumbnail. AVIF at low quality smooths fine texture instead: grain, fabric, foliage and hair lose detail while edges stay clean. That failure mode is harder to catch by eye at small size, so the check has to be at full resolution on the actual template.
- The only two measurements that count are the delivered byte count of the LCP resource, and the visual result on the template it ships in. A plugin dashboard reporting “saved 68%” is reporting on files, not on what a browser downloaded after the edge finished negotiating format.
If you run a conversion pass, keep a small fixed set of representative images from the real library: one face, one product on white, one screenshot with small text, one wide landscape. Encode those at each candidate setting and look at them. That is a twenty minute job that prevents shipping a library-wide setting that destroys product photography.
5. Edge resizing: moving the work, not removing it
Edge transformation takes resizing and format negotiation off the PHP origin. On Cloudflare that means a URL of the form https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>, where the source is an absolute path on the origin or an absolute URL, and the options are comma separated. The one that matters here is format=auto, which serves the most efficient format the requesting browser accepts.
Three things the documentation says that change how you plan the work:
- It must be enabled per zone in the dashboard before any transformation URL resolves on that hostname. This is the single most common reason a correctly written URL returns the unresized original.
- Source images are restricted to the same zone by default. Accepted sources default to an allowed-origins list, and Cloudflare documents that separately from the URL format: a separate media subdomain or an S3 bucket has to be added to that list, or the zone switched to any origin.
format=autocan decline AVIF. Because AVIF encoding is an order of magnitude slower, Cloudflare falls back to WebP or JPEG when an image is too large to encode quickly. Your hero may therefore be AVIF in testing and WebP in production, depending on the source dimensions.
Edge resizing does not remove the need for correct srcset and sizes. The browser still picks a candidate before it makes a request, so wrong markup requests a wrong width and the edge dutifully generates exactly that wrong width. Edge resizing replaces the fixed WordPress size set with arbitrary widths; it does not replace the selection logic.
6. Serving the format: picture elements and fallbacks
Core writes a single src and a srcset. It does not write <picture>, which means core alone cannot serve AVIF to browsers that accept it and JPEG to those that do not, from the same markup. You get one format per generated size.
There are two workable answers.
Content negotiation at the edge or the server. The CDN reads the Accept header and serves AVIF, WebP or JPEG from one URL. This keeps the HTML simple, and it is what format=auto does. The cost is that HTML and image caching now depend on request headers, so cache keys have to account for it.
Markup-level fallback. The Modern Image Formats plugin, part of the Performance Lab programme, generates modern-format sub-sizes on upload and, since version 2.0.0, can output <picture> elements with fallbacks. Its default choice is AVIF when the hosting server supports it and WebP otherwise, which is the right default for a plugin that cannot know your stack. Its settings live under Settings > Media, including whether to keep fallback originals. As with the core filter, it applies to new uploads: existing media needs regeneration.
Performance Lab is a staging area for features intended to land in core, so its image plugins are worth treating as the current direction of travel rather than as third-party add-ons.
7. FAQ: image performance in 2026
Can I delete my old JPEGs? Not if anything still points at them. Beyond the browser, JPEG remains the safe format for email clients, Open Graph and Twitter card images, RSS consumers, and any partner feed. Keeping the fallback also costs you nothing, because the Modern Image Formats plugin generates additional sub-sizes rather than replacing the original upload.
Is SVG still relevant for icons? Yes, and for a reason that has nothing to do with compression: SVG is resolution independent, so one file covers every display density with no
srcset. WordPress core does not allow SVG uploads by default because SVG can carry script. If you enable it, sanitise on upload rather than trusting the uploader.Should I use progressive or interlaced JPEG? It is a minor lever next to format and width. Progressive JPEG improves perceived loading on slow connections, but for an LCP image the browser is waiting on the full decode anyway, and for below-fold images the user is not looking. Spend the effort on
sizescorrectness first.My host cannot encode AVIF. What now? Use WebP as the output format through
image_editor_output_formator the Modern Image Formats setting, and check Site Health after any host migration. WebP support is far more widely available in older Imagick and LibGD builds, and the difference between JPEG and WebP is much larger than the difference between WebP and AVIF.Does AVIF help if my LCP is text? No, and that is worth checking before any of this work is scheduled. Run the page through field data and confirm which element is actually the LCP. Images are the LCP on 68% of mobile pages, which also means they are not on the other 32%.
8. Conclusion: the gap is delivery, not format
The formats are settled. AVIF has 94.67% browser support and native WordPress support since 6.5, JPEG XL does not have either and will not this year, and WebP is the fallback that keeps working everywhere. Nothing in that list is a decision that still needs research.
What is unsettled on almost every site is everything between the upload and the paint: whether sub-sizes are being generated in a modern format at all, whether sizes describes the real layout, whether the LCP element is one core can see, and whether the edge is quietly serving WebP because the source was too large to encode. That is the list to walk, in that order, with a measurement at each step. The 1% adoption figure in the Web Almanac is what happens when a site stops at “we installed an image plugin”.
If images are dragging down your Core Web Vitals, the useful first artefact is a short inventory: the affected templates, where each image comes from, and the current LCP element and timing per template. We work through exactly that list as part of WordPress speed optimization.






