Modern image optimisation for WordPress in 2026

Modern image optimisation for WordPress in 2026

Last verified: September 20, 2026
13 min read
Guide
Core Web Vitals
UI/UX designer

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:

BrowserJPEG XL status
Safari (macOS, iOS)Partial support, version 17 and later
Chrome, EdgeDisabled by default. Chromium enabled it, removed it, then reintroduced it as disabled-by-default in Chrome 145
FirefoxDisabled by default
Android Chrome, Samsung InternetNo 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 the wp_min_priority_img_pixels filter.
  • loading="lazy" is omitted for images near the top of the document. The threshold filter is wp_omit_loading_attr_threshold, whose default rose from 1 to 3 in 6.3.
  • The function never emits fetchpriority="high" and loading="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:

  1. The LCP element is not an img tag core rendered. A CSS background image, a video poster, a page builder that writes markup outside wp_filter_content_tags(). Core cannot prioritise what it cannot see, so the hero arrives after the stylesheet that references it.
  2. 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_quality receives the default quality, the mime type and the image dimensions, so it can vary quality by format and by size. jpeg_quality receives only the quality and a context string such as image_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=auto can 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

  1. 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.

  2. 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.

  3. 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 sizes correctness first.

  4. My host cannot encode AVIF. What now? Use WebP as the output format through image_editor_output_format or 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.

  5. 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.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Article FAQ

Frequently asked questions

Practical answers to apply the topic in real execution.

SEO-readyGEO-readyAEO-ready3 Q&A
Is WebP outdated in 2026?#
No. WebP is the correct output format on any host whose Imagick or LibGD build cannot encode AVIF, and the Modern Image Formats plugin falls back to it automatically for exactly that reason. AVIF compresses better at the same visual quality, but it also encodes far more slowly, which matters on shared hosting and at the CDN edge.
How does image optimisation affect LCP?#
An image is the Largest Contentful Paint element on 68% of mobile pages according to the 2024 Web Almanac, where the median largest image on mobile is 135 KB. Format alone does not fix LCP: if the browser discovers the image late, or picks a wider srcset candidate than the layout needs, the smaller file still arrives too late to help.
Should I use lazy loading for all images?#
No, and since WordPress 6.3 you usually should not do it by hand. wp_get_loading_optimization_attributes() skips loading="lazy" on the first images in the document, controlled by wp_omit_loading_attr_threshold whose default is 3, and marks the first qualifying image fetchpriority="high". The case that still breaks is an LCP image core cannot see, such as a CSS background.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles