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
Pick file
User selects an image in the block editor
- 2
Process
wasm-vips resizes, encodes, and builds sub-sizes in a Worker
- 3
Upload
Original plus each size POSTs independently
- 4
Sideload
REST registers sizes on the attachment
- 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, finalizePOST /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_imageand 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_formatis 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.
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:
- Context
'create'on the initial upload (before sub-sizes exist). - Context
'update'afterPOST /wp/v2/media/{id}/finalizewhen 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):
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
Assume every browser processes images client-side
Chromium-only for the full wasm-vips pipeline; others fall back to PHP
HEIC works on every OS because WordPress 7.1 exists
Chromium on macOS / Windows with HEVC; Safari on macOS. Platform codecs matter.
Run expensive CDN sync only on create
Handle create and update; make the callback idempotent
Next: Abilities API Changes in WordPress 7.1 — 7.0 shipped abilities; 7.1 makes them usable by clients.
Get the next one in your inbox. Practical tips, no fluff.
More tutorials
View all- WordPress
How to Prepare Your Plugin or Theme for WordPress 7.1
A WordPress 7.1 compatibility checklist: iframed editor, client-side media hooks, Abilities public flag, jQuery UI 1.14.2, plus features that did not ship (React 19, collaboration).
22 min
- WordPress
What's New in WordPress 7.1 for Developers
WordPress 7.1 field guide for developers: client-side media, Abilities API, always-iframed editor, SVG Icon API, global styles, and what to test before users upgrade.
18 min
- WordPress
Abilities API Changes in WordPress 7.1
WordPress 7.1 upgrades the Abilities API: a unified public flag, wp_get_abilities() filtering, execution lifecycle hooks, JSON Schema for clients, and typed REST input.
25 min