Block metadata viewScriptModule field in 6.5

The BlockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. now recognizes the viewScriptModule field in block.json metadata. This provides an experience for script modules that’s analogous to what scripts and the viewScript field currently provide. The script module declared in viewScriptModule will be enqueued when a block is rendered for viewing on the frontend.

This new field is important so that developers can use script modules on the frontend. Scripts cannot depend on script modules, so script modules are necessary to use script modules like the Interactivity API (via the @wordpress/interactivity script module).

Read more: Block metadata viewScriptModule field in 6.5

This post assumes a basic understanding of the Script Modules API, read more about it here.

Getting started

You can generate a simple block pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party using the @wordpress/create-block-interactive-template package:

npx @wordpress/create-block --template @wordpress/create-block-interactive-template

This will generate an interactive block from a template that uses viewScriptModule.

Usage

Add a viewScriptModule field to the metadata in block.json that points to the script module.

Here’s what a simple plugin might look like:

myplugin/
├── build/
│ ├── block.json
│ ├── …snip…
│ ├── view.asset.php
│ └── view.js
└── plugin.php

The only code in the plugin.php file calls register_block_type for the block:

add_action( 'init', function() {
register_block_type( __DIR__ . '/build/block.json' );
} );

The relevant metadata in build/block.json includes viewScriptModule:

{
"…snip…": "…",
"viewScriptModule": "file:./view.js"
}

build/view.js is the script module that depends on @wordpress/interactivity:

import { getContext, store } from '@wordpress/interactivity';

store( 'myplugin/demo', {
increment() {
getContext().val += 1;
},
decrement() {
getContext().val -= 1;
},
} );

The module asset file build/view.asset.php declares the view script module’s dependency on @wordpress/interactivity:

<?php return array(
'dependencies' => array('@wordpress/interactivity'),
);

REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/.

A new field view_script_module_ids has been added to the Block Types REST API. The field is a list of the view script module IDs associated with a block type.

Tooling

The @wordpress/scripts package is the standard way of compiling JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. assets for use in WordPress. Experimental functionality has been added to the build and start scripts to support compiling modules. It can be enabled using --experimental-modules on the command line, for example:

 wp-scripts build --experimental-modules

Script module compilation with @wordpress/scripts does not support using your own webpack file at this time.

The example plugin above was compiled using the experimental module build with @wordpress/scripts.

@wordpress/dependency-extraction-webpack-plugin has been updated to produce script module asset files. No special configuration is required. See the package documentation for details.

Relevant links

Props

#6-5, #dev-notes, #dev-notes-6-5

Updates to Block Hooks in 6.5

First introduced in WordPress 6.4, BlockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. HooksHooks In WordPress theme and development, hooks are functions that can be applied to an action or a Filter in WordPress. Actions are functions performed when a certain event occurs in WordPress. Filters allow you to modify certain functions. Arguments used to hook both filters and actions look the same. offer an extensibility mechanism for Block Themes that allows extenders to inject a dynamic block in a location specified by an “anchor” block and a relative position. (For example, before or after the Post Content block.)

WordPress 6.5 updates Block Hooks to make them more widely useful and includes some frequently requested new features.

New features

Modified layouts

Previously restricted to layouts – templates, template parts, and patterns – that didn’t have any user modifications, Block Hooks now also work with modified layouts. This works out of the box; extenders do not need to update their code to benefit from this. Block Hooks will continue to respect user customizations. If a user moves or deletes a hooked block in the Site Editor, the Block Hooks mechanism will not re-insert it, thus preserving the user’s express intentions.

Navigation block

Additionally, it is now possible to inject hooked blocks into the Navigation block. Previously, a hooked block could only be added before or after the Navigation block, but now it can also be added as its first or last child.

As a consequence, the hooked_block_types filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output.’s $context argument can now also be a WP_Post object with its post type set to wp_navigation.

This means that a negative condition, such as if ( ! $context instanceof WP_Block_Template ), is no longer sufficient to conclude that $context is an array (i.e., a pattern). Instead, use an affirmative check, such as if ( is_array( $context ) ).

The following example adds a (hypothetical) Shopping Cart block as the Navigation block’s last child, after ensuring that the block isn’t already present elsewhere in the Navigation menuNavigation Menu A theme feature introduced with Version 3.0. WordPress includes an easy to use mechanism for giving various control options to get users to click from one place to another on a site.:

function add_shopping_cart_block_to_navigation_block( $hooked_block_types, $relative_position, $anchor_block_type, $context ) {
	// Is $context a Navigation menu?
	if ( ! $context instanceof WP_Post || 'wp_navigation' !== $context->post_type ) {
		return $hooked_block_types;
	}

	// Does the Navigation menu already contain the block?
	if ( str_contains( $context->post_content, '<!-- wp:my-ecommerce/shopping-cart' ) ) {
		return $hooked_block_types;
	}

	if ( 'last_child' === $relative_position && 'core/navigation' === $anchor_block_type ) {
		$hooked_block_types[] = 'my-ecommerce/shopping-cart';
	}
	return $hooked_block_types;
}
add_filter( 'hooked_block_types', 'add_shopping_cart_block_to_navigation_block', 10, 4 );

New filters

In order to offer more fine-grained control over hooked blocks, two new filters have been introduced: hooked_block and hooked_block_{$hooked_block_type}. Both share the following signature:

  • $parsed_hooked_block (array|null) – The hooked block, in parsed block array format, or null, indicating that a previous filter has suppressed the injection of the given block.
  • $hooked_block_type (string) – The hooked block type name.
  • $relative_position (string) – The relative position of the hooked block (can be one of before, after, first_child, or last_child).
  • $parsed_anchor_block (array) – The anchor block, in parsed block array format.
  • $context (WP_Block_Template|WP_Post|array) – The block template, template part, wp_navigation post type, or pattern that the anchor block belongs to.

One of the most frequently requested features for Block Hooks was the ability to set a hooked block’s attributes. While it is generally advised to choose default values for block attributes that work well when the block is used as a hooked block, it is now possible to set custom values for its attributes via these new filters.

In addition, the filters allow setting a hooked block’s inner blocks (and, by extension, wrapping it inside of another block), or suppressing the hooked block altogether (by returning null).

Finally, the filters are also given the anchor block instance as an argument, so it’s possible to, for example, set the hooked block’s attributes based on the anchor block’s:

function set_block_layout_attribute_based_on_adjacent_block( $hooked_block, $hooked_block_type, $relative_position, $anchor_block ) {
	// Has the hooked block been suppressed by a previous filter?
	if ( is_null( $hooked_block ) ) {
		return $hooked_block;
	}

	// Is the hooked block adjacent to the anchor block?
	if ( 'before' !== $relative_position && 'after' !== $relative_position ) {
		return $hooked_block;
	}

	// Does the anchor block have a layout attribute?
	if ( isset( $anchor_block['attrs']['layout'] ) ) {
		// Copy the anchor block's layout attribute to the hooked block.
		$hooked_block['attrs']['layout'] = $anchor_block['attrs']['layout'];
	}

	return $hooked_block;
}
add_filter( 'hooked_block_my/like-button', 'set_block_layout_attribute_based_on_adjacent_block', 10, 4 );

For more examples, refer to the snippets found in the description of the PR that introduced the new filters.

Plugins block inspector panel toggles

The toggles for each hooked block in the block inspector’s “Plugins” panel have been updated to fix a confusing behavior that would remove the toggle in certain cases after it was deactivated. Furthermore, toggles are now shown for blocks added by the hooked_block_types filter; note that due to technical limitations, this currently only works for layouts that have user modifications.

A screenshot showing a Navigation block selected in the Site Editor. The block contains 'News', 'About', and 'Logout' links, and a shopping cart icon.

The Block Inspector sidebar is open. It is showing the 'Plugins' panel with toggles for the hooked blocks: one for the Shopping Cart, and one for the Login/out link.

Under the hood

The major technical challenge to making Block Hooks work with modified layouts was to ensure they would continue respecting user customizations to a given layout. That is, when a block was persisted, moved, or deleted by the user in the Site Editor, it wouldn’t end up being re-inserted by the Block Hooks mechanism.

It turned out that this problem could be solved by essentially a “finer-grained” version of the same principle (i.e., modified vs. unmodified). This is achieved by storing information about hooked block types in an ignoredHookedBlocks array inside the global metadata attribute on the anchor block. Specifically, it works via the two following operations:

On write (i.e., persisting a modified layout to the database): All hooked block types that were present during the edit are persisted to their respective anchor block’s ignoredHookedBlocks arrays. (Note that this is regardless of the nature of the edit: It works the same both for when the hooked block is persisted and when it is removed.)

On read (i.e., rendering on the front end or loading into the Site Editor): The Block Hooks mechanism will now look for the presence of a given hooked block type inside its anchor block’s ignoredHookedBlocks array. If a hooked block’s type name is found in that array, it will be ignored (i.e., not injected).

See this Trac ticket for more information on how this solution was conceived.

For controlled inner blocks: For inner blocks that aren’t present inside the containing block but loaded separately at runtime, the same ignoredHookedBlocks array is stored, but in a new post metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress. field _wp_ignored_hooked_blocks instead of in the containing block’s attributes. This is how hooked blocks work in the Navigation block with its external wp_navigation post.


With these new features in place and several important limitations lifted, Block Hooks can now be used across a large number of layouts, without paying heed to whether the user has modified a layout or not. Specialized filters allow you to fine-tune hooked blocks by setting their attributes and inner blocks while also taking information about the anchor block instance into account when doing so. This makes Block Hooks a more intuitive and powerful tool for extending Block Themes.

We’re looking forward to seeing what extenders will create with this APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways.!

Props to @dmsnell, @gziolo, @ndiego, and @leonnugraha for peer review.

#6-5, #dev-notes, #dev-notes-6-5

Performance improvements for registering block variations with callbacks

Background

In WordPress 5.8, the APIAPI An API or Application Programming Interface is a software intermediary that allows programs to interact with each other and share data in limited, clearly defined ways. for registering blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. variations was introduced, bringing new capabilities to developers. However, it soon became evident that generating these variations had a non-trivial performance cost, particularly when used for template-part and navigation-link in WordPress 5.9 and post-terms in WordPress 6.1.

The coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. of this issue was that these variations were being generated on every page load due to their registration using the init hook. This was inefficient, as the variations were only needed in the editor or for returning block data in the REST APIREST API The REST API is an acronym for the RESTful Application Program Interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. It is how the front end of an application (think “phone app” or “website”) can communicate with the data store (think “database” or “file system”) https://developer.wordpress.org/rest-api/.. This redundancy in loading led to unnecessary performance overhead, underscoring the need for a more streamlined approach.

What is changed

In WordPress 6.5, we’ve introduced $variation_callback as a new attribute in WP_Block_Type. This feature allows developers to register variation-building functions as callbacks rather than building variations before registration. With this approach, variations are constructed from this callback only when they are accessed for the first time. 

Consequently, the WP_Block_Type::get_variations() method was introduced as the primary means to access variations. This method skillfully checks for a callback in variation_callback and triggers the callback, but only during the first access of the variations. This efficient approach ensures that the callback is executed just once, optimizing performance by avoiding redundant processing on subsequent accesses. Additionally, this strategy aids in lazy loading, as variations no longer need to be loaded during registration.

Moreover, it integrates the get_block_type_variations filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output., which provides a versatile mechanism for developers to modify the resulting variations. These enhancements reflect WordPress’s ongoing commitment to improving its developer-centric features and maintaining compatibility with existing functionalities.
In WordPress 6.5, a significant alteration is made to WP_Block_Type::$variations. The notable change involved transitioning the visibility of variations from public to private. This modification was specifically designed to necessitate the use of the PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher magic __get method for accessing variations only via WP_Block_Type::get_variations().

Please note that the modifications to WP_Block_Type::$variations may introduce certain limitations that require developer attention. These are detailed in the “Developer Action” section below.

Lazy loading variations using variation_callback

To facilitate the lazy loading of variations, we can replace the traditional method of loading variations with a callback. This approach is exemplified in the case of GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ PR #56952, which is aimed at enhancing performance.

Previously, template-part variations were loaded as follows:

register_block_type_from_metadata(
    __DIR__ . '/template-part',
    array(
        'render_callback'    => 'render_block_core_template_part',
        'variations'         => build_template_part_block_variations(),
    )
);

To optimize this, we have transitioned to using a variation_callback:

register_block_type_from_metadata(
    __DIR__ . '/template-part',
    array(
        'render_callback'    => 'render_block_core_template_part',
        'variation_callback' => 'build_template_part_block_variations',
    )
);

It’s important to note that if both variations and variation_callback are defined, like in the following example:

register_block_type_from_metadata(
'block-name',
array(
'variations' => array( 'test-variation' => array( 'test-variations' ) ),
'variation_callback' => 'get_test_variations',
)
);

In this scenario, the variations array takes precedence, and the variation_callback will not be called.

Developer action required

With the addition of variations to the existing magic function framework in WP_Block_Type, a significant limitation emerges: the inability to modify variations by reference directly. This change impacts how variations were traditionally updated.

Consider these examples of updating variations by reference, which will no longer function as expected in WordPress 6.5:

$block_type->variations[] = array( 'test' => array( 'test' ) );
// or
$block_type->variations['example1'] = array( 'test' );

To overcome this limitation in WordPress 6.5, a workaround involving a temporary variable can be used. Here’s how you can modify the temporary variable and then reassign it back to $block_type->variations:

$variations = $block_type->variations;$variations[] = array( 'test' => array( 'test' ) );
$block_type->variations = $variations;
// Similarly$variations = $block_type->variations;
$variations['example1'] = array( 'test' );$block_type->variations = $variations;

Alternatively, to align with the latest updates and best practices in WordPress, you might consider using the get_block_type_variations filter (reference link).

Utilizing the get_block_type_variations filter for enhanced variation management

The introduction of the get_block_type_variations filter in WordPress 6.5 allows for the manipulation and customization of the variations array, providing greater flexibility in handling block variations.

Here’s an implementation example of the get_block_type_variations filter:

function modify_block_type_variations( $variations, $block_type ) {
// Example: Add a new variation
if ( 'block-name' === $block_type->name ) {
$variations[] = array(
'name' => 'new-variation',
'title' => __( 'New Variation', 'textdomain' ),
// Other variation properties...
);
}

return $variations;
}
add_filter( 'get_block_type_variations', 'modify_block_type_variations', 10, 2 );

In this example, the modify_block_type_variations function is hooked to the get_block_type_variations filter. It checks if the block type is ‘block-name‘ and then adds a new variation to the variations array. This function then returns the modified variations array.

By leveraging this filter, developers can dynamically adjust the variations according to specific requirements or conditions.

Please refer to #59969 for additional details on these changes.

Props to @adamsilverstein, @westonruter, @joemcgill, and @leonnugraha for reviewing this post.

#6-5, #block-api, #dev-notes, #dev-notes-6-5

I18N Improvements in 6.5 (Performant Translations)

Various internationalization (i18n) improvements are in WordPress 6.5, and this developers note will focus on these.

New localization system with improved performance

Over the past year, WordPress contributors have meticulously analyzed performance of the existing i18ni18n Internationalization, or the act of writing and preparing code to be fully translatable into other languages. Also see localization. Often written with a lowercase i so it is not confused with a lowercase L or the numeral 1. Often an acquired skill. system in WordPress and ultimately created a new Performant Translations feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins. that provided a completely overhauled system with significantly better performance. After thousands of betaBeta A pre-release of software that is given out to a large group of users to trial under real conditions. Beta versions have gone through alpha testing in-house and are generally fairly close in look, feel and function to the final product; however, design changes often occur as part of the process. testers and a merge announcement late last year, this new library is now included in WordPress 6.5! See #59656 for all the details.

The Performant Translations pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party is still useful and will continue to be maintained to build on top of the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. solution with a distinct additional feature. As is already the case today, the plugin will automatically convert any .mo files to PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher files if a PHP file does not currently exist. This is useful for sites where translations are not coming from translate.wordpress.org or only exist locally on that server.

This new library is faster at loading binary .mo files and uses less memory. It even supports loading multiple locales at the same time, which makes locale switching faster. In addition to that, it supports translations contained in PHP files, avoiding a binary file format and leveraging OPCache if available.

The new library is so fast, in fact, that it paves the way for the Preferred Languages feature plugin to merge translations of multiple locales by default starting with WordPress 6.5.

While this is in large part a silent and backward-compatible under-the-hood change, there are still a few things to be aware of:

New .l10n.php translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. file format

When downloading language packs from WordPress.orgWordPress.org The community site where WordPress code is created and shared by the users. This is where you can download the source code for WordPress core, plugins and themes as well as the central location for community conversations and organization. https://wordpress.org/, there will be a new .l10n.php file in addition to the .mo and .po files you are already familiar with. If an .mo translation file has a corresponding .l10n.php file, the latter will be loaded instead, making things even faster and use even less memory.

This is a progressive enhancementenhancement Enhancements are simple improvements to WordPress, such as the addition of a hook, a new feature, or an improvement to an existing feature., so if there’s only an .mo file but no PHP file, translations will still be loaded as expected. However, the opposite is also true! So you can theoretically use only .l10n.php translation files in your project and features such as the just-in-time translation loading continue to work. Right now, WordPress still expects corresponding .po and .mo files for things like update checks. However, this limitation will be addressed in the future, see #60554 for more information.

Note: if you don’t see any .l10n.php translation files in wp-content/languages yet, it might be that the language pack hasn’t been updated in a while, i.e. there were no new translations.

Here’s an example of a PHP translation file as supported by WordPress 6.5:

<?php
return [
'project-id-version' => 'WordPress - 6.5.x - Development',
'report-msgid-bugs-to' => 'polyglots@example.com',
'messages' =>
[
'original' => 'translation',
'contextEOToriginal with context' => 'translation with context',
'plural0' => 'translation0' . "\0" . 'translation1',
'contextEOTplural0 with context' => 'translation0 with context' . "\0" . 'translation1 with context',
'Product' => 'Produkt' . "\0" . 'Produkte',
],
];

Note: EOT here stands for the “End of Transmission” character (U+0004, or "\4" in PHP). It’s the same delimiter as in gettext used to glue the context with the singular string.

Generating PHP translation files

If you would like to generate these PHP translation files yourself, version 4.0 of GlotPress, the plugin that powers translate.WordPress.org, already supports the new .l10n.php format.

In addition to that, WP-CLI 2.10.0 (i18n-command 2.6.0) provides a new wp i18n make-php command to create these PHP files from a given .po file. Examples:

# Create PHP files for all PO files in the current directory.
$ wp i18n make-php .

# Create a PHP file from a single PO file in a specific directory.
$ wp i18n make-php example-plugin-de_DE.po languages

If you are developing a WordPress plugin that deals with translations, you can also use the new WP_Translation_File class to convert an .mo file into a PHP file. Example:

$contents = WP_Translation_File::transform( $mofile, 'php' );
if ( $contents ) {
file_put_contents( $path_to_php_file, $contents );
}

New filters to customize this behavior

If you would like to disable the support for PHP files for some reason; for example, if you don’t have any yet in your project and want to prevent the extra file lookup operation, you can use the new translation_file_format filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. to change the preferred format (default is php) like so:

add_filter(
'translation_file_format',
static function () {
return 'mo';
}
);

The existing load_textdomain_mofile filter can still be used to filter the .mo file path for loading translations for a specific text domain. However, it only works for .mo files. To filter the path for a translation file, be it a .l10n.php or a .mo file, use the new load_translation_file filter.

Working with the $l10n global variable

Previously, when loading translations, WordPress would store an instance of the MO class in the $l10n global variable. With WordPress 6.5, this will be an instance of a new WP_Translations class that acts as a shim with similar characteristics. If your project directly works with this global variable or the MO class in some way, this is an area to keep an eye on.

Cached list of language file paths

This another slight performance improvement but unrelated to the new localization library covered above.

In places such as get_available_languages() and WP_Textdomain_Registry, WordPress used to directly use the glob() function to retrieve all .mo files in a specific directory. This is important for just-in-time translation loading and generally knowing which translations are installed. However, on sites with a large number of language files, the glob() operation can become expensive.

Because of this, a new caching mechanism was introduced in #58919 / [57287]. The file lookup is now handled centrally in WP_Textdomain_Registry and stored in the object cache in the translations group, with the cache key having the format cached_mo_files_<hash>, where <hash> is the MD5 hash of the scanned directory, e.g. wp-content/languages. The cache is cleared whenever language packs are updated.

Also, the lookup now also scans for .l10n.php files in addition to .mo files, in case only the former exist on a site.

More questions? Please let us know

If you have any questions, please leave a comment below or file a new ticket on Trac under the I18N component if you’ve encountered any bugs.

Props to @joemcgill, @stevenlinx for review.

#6-5, #dev-notes, #dev-notes-6-5, #i18n

WordPress 6.5 adds AVIF support 

WordPress 6.5 supports AVIF, a modern image format that offers significant improvements in image quality and compression over previous formats like JPEG, PNG, and even WebP.  AVIF images can be up to 50% smaller than JPEGs while maintaining the same image quality. AVIF images also support a wide range of colors (including HDR) and produce sharper images than JPEGs, especially in areas with high detail.

From WordPress version 6.5 forward, you can upload and use AVIF images in WordPress just like you would a JPEG or PNG image today – as long as your hosting environment supports AVIF. Switching to the AVIF format for your images is likely to improve your site’s performance and your site visitor’s experience. 

How AVIF helps you

AVIF images are significantly smaller than their JPEG equivalents, so pages load more quickly and take less bandwidth to transmit. AVIF images still get all of the benefits of the responsive images, Fetch Priority, and lazy loading that WordPress supports by default. Finally, AVIFs are supported in all major browsers, so most sites can start using them today.

Creating AVIF images

Many image editing tools support exporting to AVIF. You can also use command line conversion tools or web based open sourceOpen Source Open Source denotes software for which the original source code is made freely available and may be redistributed and modified. Open Source **must be** delivered via a licensing model, see GPL. tools like Squoosh. Once you save your images as AVIF, upload them to WordPress and use them like you would any other image. WordPress can also create AVIFs for you automatically, for more details, see the FAQ item below.

Using AVIF images in WordPress

AVIF images work like any other image format in WordPress, with a few important notes:

AVIF in WordPress depends on support in your web server’s image processing library (WordPress has built-in support for both Imagick and LibGD for image processing). You can check for AVIF support in wp-adminadmin (and super admin) by visiting Tools -> Site Health, clicking the “Info” tab and expanding the “Media Handling” section, then finally looking for “AVIF” in the list of supported formats.

If your audience includes a significant number of users on an unsupported browser, either avoid using AVIF images, or enqueue a browser polyfill.

FAQ

Q: How do I adjust the compression level used for generated AVIF images?

The wp_editor_set_quality filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. can be used to set the quality setting. The passed mime type enables setting by type, for example:

// Use a quality setting of 75 for AVIF images.
function filter_avif_quality( $quality, $mime_type ) {
if ( 'image/avif' === $mime_type ) {
return 75;
}
return $quality;
}
add_filter( 'wp_editor_set_quality', 'filter_avif_quality', 10, 2 );

How can I output lossless AVIF images?

Using a compression level of 100 will set AVIF to its lossless mode.

Q: Can WordPress create AVIF images when I upload JPEGs?

Yes. Developers can use the image_editor_output_format filter to specify this type of transformation for uploads. Here is an example:

// Output AVIFs for uploaded JPEGs
function filter_image_editor_output_format( $formats ) {
$formats['image/jpeg'] = 'image/avif';
return $formats;
}
add_filter( 'image_editor_output_format', 'filter_image_editor_output_format' );

If I use WordPress multisitemultisite Used to describe a WordPress installation with a network of multiple blogs, grouped by sites. This installation type has shared users tables, and creates separate database tables for each blog (wp_posts becomes wp_0_posts). See also network, blog, site, will all my sites work with AVIF images?

No. Multisite stores the file types that users are allowed to upload when a site is created. We are still working on improving this in #53167. In the meantime, to ensure all existing sites on a networknetwork (versus site, blog) allow AVIF files, you can use the site_option filter in a network mu-pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party to add avif to the allowed file types for all network sites:

// Ensure all network sites include AVIF support.
function filter_site_option_upload_filetypes( $filetypes ) {
$filetypes = explode( ' ', $filetypes );
if ( ! in_array( 'avif', $filetypes, true ) ) {
$filetypes[] = 'avif';
}
return implode( ' ', $filetypes );
}
add_filter( 'site_option_upload_filetypes', 'filter_site_option_upload_filetypes' );


Thanks to @stevenlinx and @westonruter for reviewing this post.

#6-5, #core-images, #dev-notes, #dev-notes-6-5, #images