WordPress Image Optimisation Pipeline: WebP, AVIF, and Responsive Images

WordPress 5.8+ generates WebP versions of uploaded images automatically (when the GD or Imagick extension supports it), and WordPress 6.5 added experimental AVIF support. A complete image optimisation pipeline combines format generation, quality tuning, responsive srcset attribute output, and a <picture> element fallback for maximum browser compatibility and minimum file size.

Problem: WordPress's default image handling uploads JPEG and PNG files as-is — large original images are served to all visitors, there is no automatic WebP or AVIF generation, and the theme lacks responsive image sizes appropriate for each viewport.

Solution: Add WebP conversion to the upload pipeline using wp_generate_attachment_metadata and the imagick PHP extension. Register custom image sizes with add_image_size() for each breakpoint. Use the wp_calculate_image_srcset filter to add WebP sources to srcset, and add AVIF as a progressive enhancement via a <picture> source element.


The code below enables WebP/AVIF output at the correct quality settings, adds a custom image size for above-the-fold hero images, outputs a <picture> element with AVIF and WebP sources, and defers off-screen images with native lazy loading.


 82 );   // JPEG quality
add_filter( 'wp_image_editors', function ( array $editors ): array {
    // Prefer Imagick over GD for better AVIF/WebP support
    return array_unique( array_merge( [ 'WP_Image_Editor_Imagick' ], $editors ) );
} );

// Enable AVIF output (WordPress 6.5 experimental)
add_filter( 'wp_uploading_image_mime_type', function ( string $mime ): string {
    return $mime;  // hook exists; AVIF generation happens via Imagick automatically
} );

// 2. Register a hero image size (large dimensions, high quality)
add_action( 'after_setup_theme', function () {
    add_image_size( 'hero-2x', 2560, 1440, false );
    add_image_size( 'hero-1x', 1280, 720,  false );
    add_image_size( 'card',     640, 400,  true  );
} );

// 3. Output a  element with AVIF + WebP + JPEG fallback
function my_responsive_picture( int $attachment_id, string $size = 'large', array $attr = [] ): string {
    $jpeg_src    = wp_get_attachment_image_url( $attachment_id, $size );
    $jpeg_srcset = wp_get_attachment_image_srcset( $attachment_id, $size );
    $sizes_attr  = wp_get_attachment_image_sizes( $attachment_id, $size );
    $alt         = esc_attr( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) );

    // Look for WebP and AVIF variants generated by WordPress
    $metadata = wp_get_attachment_metadata( $attachment_id );
    $sources  = [];

    foreach ( ( $metadata['sources'] ?? [] ) as $mime => $source_data ) {
        if ( in_array( $mime, [ 'image/avif', 'image/webp' ], true ) ) {
            $dir = dirname( get_attached_file( $attachment_id ) );
            $url_dir = dirname( $jpeg_src );
            $srcset = implode( ', ', array_map(
                fn( $w, $d ) => $url_dir . '/' . $d['file'] . " {$w}w",
                array_keys( $source_data ),
                $source_data
            ) );
            $sources[ $mime ] = $srcset;
        }
    }

    $picture = '';
    foreach ( $sources as $mime => $srcset ) {
        $picture .= sprintf(
            '',
            esc_attr( $mime ), esc_attr( $srcset ), esc_attr( $sizes_attr )
        );
    }
    $picture .= sprintf(
        '%s',
        esc_url( $jpeg_src ),
        esc_attr( $jpeg_srcset ),
        esc_attr( $sizes_attr ),
        $alt
    );
    $picture .= '';
    return $picture;
}


NOTE: WordPress generates WebP/AVIF sub-sizes only for newly uploaded images — existing images need to be regenerated with wp media regenerate --yes (WP-CLI) after enabling the image editors; this can be a long-running operation on sites with thousands of media items, so run it during off-peak hours.