Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/upload-media/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### New Features

- Add `image/jxl` to `CLIENT_SIDE_SUPPORTED_MIME_TYPES` and `jxl` to the supported output formats, enabling client-side JPEG XL processing.

## 0.30.0 (2026-04-29)

### Enhancement
Expand Down
1 change: 1 addition & 0 deletions packages/upload-media/src/store/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const CLIENT_SIDE_SUPPORTED_MIME_TYPES: readonly string[] = [
'image/gif',
'image/webp',
'image/avif',
'image/jxl',
] as const;

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/upload-media/src/store/private-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,14 @@ export function finishOperation(
};
}

const VALID_IMAGE_FORMATS = [ 'jpeg', 'webp', 'avif', 'png', 'gif' ] as const;
const VALID_IMAGE_FORMATS = [
'jpeg',
'webp',
'avif',
'png',
'gif',
'jxl',
] as const;

/**
* Checks if a format string is a valid ImageFormat.
Expand Down
2 changes: 1 addition & 1 deletion packages/upload-media/src/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,4 @@ export interface SideloadAdditionalData extends AdditionalData {
image_size: string | string[];
}

export type ImageFormat = 'jpeg' | 'webp' | 'avif' | 'png' | 'gif';
export type ImageFormat = 'jpeg' | 'webp' | 'avif' | 'png' | 'gif' | 'jxl';
3 changes: 2 additions & 1 deletion packages/upload-media/src/store/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export async function vipsConvertImageFormat(
| 'image/png'
| 'image/webp'
| 'image/avif'
| 'image/gif',
| 'image/gif'
| 'image/jxl',
quality: number,
interlaced?: boolean
) {
Expand Down
4 changes: 4 additions & 0 deletions packages/vips/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### New Features

- Bundle `vips-jxl.wasm` to enable client-side JPEG XL (JXL) encoding and decoding.

## 1.5.0 (2026-04-29)

## 1.4.0 (2026-04-15)
Expand Down
24 changes: 20 additions & 4 deletions packages/vips/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import VipsModule from 'wasm-vips/vips.wasm';
// @ts-expect-error - WASM files are inlined as base64 data URLs at build time
import VipsHeifModule from 'wasm-vips/vips-heif.wasm';

// @ts-expect-error - WASM files are inlined as base64 data URLs at build time
import VipsJxlModule from 'wasm-vips/vips-jxl.wasm';

/**
* Internal dependencies
*/
Expand Down Expand Up @@ -41,10 +44,9 @@ async function getVips(): Promise< typeof Vips > {
}

vipsPromise = Vips( {
// Load HEIF dynamic module for HEIF/HEIC and AVIF format support.
// JXL is omitted as WordPress Core does not currently support it.
// It can be re-added when Core adds JXL support.
dynamicLibraries: [ 'vips-heif.wasm' ],
// Load HEIF dynamic module for HEIF/HEIC and AVIF format support,
// and JXL dynamic module for JPEG XL format support.
dynamicLibraries: [ 'vips-heif.wasm', 'vips-jxl.wasm' ],
locateFile: ( fileName: string ) => {
// WASM files are inlined as base64 data URLs at build time,
// eliminating the need for separate file downloads and avoiding
Expand All @@ -53,6 +55,8 @@ async function getVips(): Promise< typeof Vips > {
return VipsModule;
} else if ( fileName.endsWith( 'vips-heif.wasm' ) ) {
return VipsHeifModule;
} else if ( fileName.endsWith( 'vips-jxl.wasm' ) ) {
return VipsJxlModule;
}
return fileName;
},
Expand Down Expand Up @@ -154,6 +158,12 @@ export async function convertImageFormat(
saveOptions.effort = 2;
}

// JXL default effort of 7 is too slow for interactive use.
// Use 3 for a good balance of speed and compression.
if ( 'image/jxl' === outputType ) {
saveOptions.effort = 3;
}

const outBuffer = image.writeToBuffer( `.${ ext }`, saveOptions );
const result = outBuffer.buffer;

Expand Down Expand Up @@ -310,6 +320,12 @@ function buildSaveOptions(
saveOptions.effort = 2;
}

// JXL default effort of 7 is too slow for interactive use.
// Use 3 for a good balance of speed and compression.
if ( 'image/jxl' === type ) {
saveOptions.effort = 3;
}

return saveOptions;
}

Expand Down
17 changes: 13 additions & 4 deletions packages/vips/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,19 @@
*/
export function supportsQuality(
type: string
): type is 'image/jpeg' | 'image/png' | 'image/webp' | 'image/avif' {
return [ 'image/jpeg', 'image/png', 'image/webp', 'image/avif' ].includes(
type
);
): type is
| 'image/jpeg'
| 'image/png'
| 'image/webp'
| 'image/avif'
| 'image/jxl' {
return [
'image/jpeg',
'image/png',
'image/webp',
'image/avif',
'image/jxl',
].includes( type );
}

/**
Expand Down
Loading