MW
Tutorial

Client-Side Media Processing in WordPress 7.1

WordPress 7.1 processes images in the browser with wasm-vips — compression, thumbnails, HEIC, AVIF, GIF-to-video — plus REST sideload, finalize, and plugin hook compatibility.

intermediate 25 min Aug 20, 2026
You have read Part 1 of this series You know how WordPress image sizes and attachment metadata work

CLIENT-SIDE MEDIA PROCESSING IN…

Until WordPress 7.1, every image upload paid PHP: GD or Imagick generated sub-sizes, rotated EXIF, converted formats, and often hit memory limits on a 12-megapixel phone photo. Client-side media processing moves that work into the browser with wasm-vips (libvips compiled to WebAssembly). The server stores files. The user’s device does the heavy lifting.

It is enabled by default in supporting browsers.

How the pipeline works

  1. 1

    Pick file

    User selects an image in the block editor

  2. 2

    Process

    wasm-vips resizes, encodes, and builds sub-sizes in a Worker

  3. 3

    Upload

    Original plus each size POSTs independently

  4. 4

    Sideload

    REST registers sizes on the attachment

  5. 5

    Finalize

    wp_generate_attachment_metadata fires with context update

Packages involved:

  • @wordpress/upload-media — queue, concurrency (max 5 uploads, max 2 image ops)
  • @wordpress/vips — wasm-vips in a Web Worker (vips.wasm + vips-heif.wasm)
  • @wordpress/media-utils — REST transport
  • @wordpress/video-conversion — animated GIF → MP4/WebM via WebCodecs + mediabunny

On PHP:

  • wp_is_client_side_media_processing_enabled() — feature gate
  • Filter wp_client_side_media_processing_enabled — opt out
  • REST: sideload POST /wp/v2/media/{id}/sideload, finalize POST /wp/v2/media/{id}/finalize

What you get for free

  • Consistent quality — same libvips output whether the host has GD, Imagick, or neither with AVIF.
  • No PHP memory failures — large images process in the browser’s memory.
  • HEIC/HEIF — iPhone photos decode in the browser and upload as JPEG. The original HEIC is kept as a companion source_image and deleted with the attachment.
  • AVIF without server AVIF — client decode bypasses the server MIME check for client-decoded uploads.
  • Gain Map HDR — UltraHDR JPEGs keep the gain map on every sub-size. Format conversion via image_editor_output_format is skipped so the map is not stripped.
  • Animated GIF → video — opaque GIFs become a companion MP4/WebM; the editor can swap to a looping muted Video block. Transparent GIFs stay images. Firefox (no WebCodecs encode) uploads the GIF unchanged.
  • Resilient uploads — sub-sizes are independent requests with retry and backoff. Offline pauses; reconnect resumes.

New client-side media processing APIs allow supported image operations to happen in the browser before a file is uploaded.

— WordPress 7.1 Field Guide

REST changes you should know

Related tickets: #64798 (dimension validation on sideload), #65262 (size-aware encode quality on attachment responses), #65481 (one sideloaded file registered under multiple image sizes).

New request knobs include generate_sub_sizes and convert_format. Attachment responses can include exif_orientation, missing_image_sizes, filename, filesize, and a size-aware image_quality field so the client honours wp_editor_set_quality / jpeg_quality per registered size.

Sizes that share dimensions (for example a theme large that matches medium_large) are deduplicated to one physical file registered under every matching size name.

Your PHP hooks still fire

The fear is “if the browser generates thumbnails, my watermark plugin dies.” It does not.

wp_generate_attachment_metadata fires twice, same as deferred big-image uploads on the server:

  1. Context 'create' on the initial upload (before sub-sizes exist).
  2. Context 'update' after POST /wp/v2/media/{id}/finalize when all client-side sideloads complete.

Write those callbacks idempotently. If finalize fails, the error is logged and the upload still succeeds — plugin failure cannot block the user.

Existing filters still apply client-side: big_image_size_threshold, image_editor_output_format, image_save_progressive, wp_image_maybe_exif_rotate, wp_editor_set_quality, jpeg_quality.

There is no client_side_supported_mime_types filter. The supported set is fixed: JPEG, PNG, GIF, WebP, AVIF.

Opting out

If your plugin cannot tolerate browser-generated files (strict server-side watermarking, a custom image editor, compliance that requires server hashes):

WP Filter wp_client_side_media_processing_enabled Priority 10 1 arg: $enabled
your-plugin.php
add_filter( 'wp_client_side_media_processing_enabled', '__return_false' );

Cross-origin isolation (Document-Isolation-Policy: isolate-and-credentialless on post, site-editor, and widgets screens for Chromium 137+) is only sent when client-side media is enabled. That isolation unlocks SharedArrayBuffer for any editor code — including your own WASM features.

Media Library: infinite scroll by default

The Media Library grid now infinite-scrolls. Each user can restore pagination in their profile. Also fixed: upload counts when adding multiple files from the post editor (#65053), and duplicate figcaption IDs when the same image has different captions (#65315).

See Media Library infinite scrolling is now enabled by default.

Browser reality

WASM pipeline
Wrong

Assume every browser processes images client-side

Right

Chromium-only for the full wasm-vips pipeline; others fall back to PHP

Firefox and older Safari still hit GD/Imagick. Test both paths.
HEIC decode
Wrong

HEIC works on every OS because WordPress 7.1 exists

Right

Chromium on macOS / Windows with HEVC; Safari on macOS. Platform codecs matter.

Windows without HEVC still fails HEIC decode.
Metadata hooks
Wrong

Run expensive CDN sync only on create

Right

Handle create and update; make the callback idempotent

Sub-sizes will not exist on the first pass.
Smart fallback is the product: unsupported browsers look unchanged. Your plugin must behave on both paths.

Next: Abilities API Changes in WordPress 7.1 — 7.0 shipped abilities; 7.1 makes them usable by clients.

You've completed this tutorial!

Get the next one in your inbox. Practical tips, no fluff.

Subscribe

Get weekly notes in your inbox

Practical tips, tutorials and resources. No spam.