Script loading changes in WordPress 6.4

Script loading strategies are now employed in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. and bundled themes to improve performance of loading scripts with defer and async attributes. Additionally, scripts on the frontend and login screen are now constructed using script helper functions, making it possible to utilize Content Security Policy to harden against any XSS vulnerabilities. This change also has back-compat implications for the obsolete use of the clean_url 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 inject defer and async attributes; plugins should now use the script loading strategies 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. instead. The full details follow.

Utilizing script loading strategies

In WordPress 6.3, the script loading strategies were introduced (#12009), enabling scripts to finally have an API for marking them to be printed with async or defer without resorting to filtering script_loader_tag (or worse clean_url, per below). For more information, refer to the dev note on Registering scripts with async and defer attributes in WordPress 6.3.

In WordPress 6.4, script loading strategies are now being employed for frontend scripts in core and bundled themes. For the most part, the defer loading strategy is used since it is more consistent in its loading behavior, in that a defer script always executes once the DOM has loaded; a script with `async` may actually 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. rendering if it is already cached. 

Additionally, scripts now loading with defer have been moved from the footer to the head so that they are discovered earlier while the document is loading and can execute sooner once the document is loaded. The changes include:

  • The defer loading strategy is being used for all block view scripts, such as the Navigation, File, and Search blocks as well as any blocks added by plugins. (#59115)
  • The defer loading strategy is also now being used for the wp-embed script which is included when there is a WordPress post embed present on the page. (#58931)
  • Frontend scripts used in bundled themes also use the defer loading strategy. (#59316)
  • The async loading strategy is used for the comment-reply script, and it continues to load in the footer since it is low priority being that comments are not visible when first viewing a post. (#58870)

If there is any theme or 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 that enqueues a script that depends on any of the above scripts (which is unlikely), the script loading strategy API considers the dependents of a delayed script so that if any of them are blocking, the delayed script will also fall back to blocking. This ensures the execution order is preserved.

Eliminating manual construction of script tags

Throughout WordPress core there have been many places where script tags are manually constructed instead of relying on the helper functions wp_print_inline_script_tag(), wp_get_inline_script_tag(), wp_print_script_tag(), and wp_get_script_tag(). In WordPress 6.4 via #58664, these functions are now employed for all scripts printed to the frontend and for scripts printed on the login screen. This includes all scripts printed via wp_enqueue_script() as well as other functions that interact with WP_Scripts, namely wp_add_inline_script() and wp_localize_script(). More details on  #59446 which continues this work throughout wp-adminadmin (and super admin).

Using these helper functions makes core easier to read and maintain with less code duplication. It also opens the door to being able to leverage Content Security Policy (CSP) for hardening WordPress against script injection attacks (XSS vulnerabilities). This is due to these functions allowing additional attributes to be added to script tags via the wp_script_attributes and wp_inline_script_attributes filters, allowing the nonce attribute to be added. Refer to an example plugin that enables Strict CSP on the frontend and login screens.

On a related note to the script loading strategies above, there is one aspect of this change that breaks a prior method for adding async or defer to scripts using the clean_url filter:

<?php
// ⚠ WARNING: Do not do this.
function defer_parsing_of_js ( $url ) {
    if ( FALSE === strpos( $url, '.js' ) ) return $url;
    if ( strpos( $url, 'jquery.js' ) ) return $url;
    return "$url' defer ";
}
add_filter( 'clean_url', 'defer_parsing_of_js', 11, 1 );

This no longer works with WordPress 6.4 because the script URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org is now escaped when constructing the HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. attributes for the script tagtag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.). So previously if it resulted in this script tag:

<script src='/wp-includes/js/underscore.js?ver=1.13.4' defer></script>

It now results in:

<script src="/wp-includes/js/underscore.js?ver=1.13.4%27%20defer"></script>

The clean_url filter (introduced in WP 2.3) never should have been used for this purpose once the script_loader_tag filter was introduced in WP 4.1. The clean_url filter runs on every single URL on the page, not just the script URLs. Also, the clean_url filter approach relied on scripts being printed with single-quoted HTML attribute values. 

Plugins continuing to use the clean_url filter in this way will find that the desired attribute is no longer injected, and that the attribute instead appears appended to the ver query parameter for the script. Plugins seeking to add defer or async attributes should instead now use the script loading strategies API. More on the dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase. Registering scripts with async and defer attributes in WordPress 6.3

Props to @webcommsat and @flixos90 for reviewing.

#6-4, #dev-notes, #dev-notes-6-4

Changes to attachment pages

As of WordPress 6.4, attachment pages for new WordPress installations are fully disabled.

Until WordPress 6.4 was released, WordPress created attachment pages by default for every attachment uploaded. On the vast majority of sites, these attachment pages don’t add any meaningful information. They do, however, exist, get indexed by search engines, and sometimes even rank in search results, leading to bad results for users and site owners.

This change introduces a wp_attachment_pages_enabled database option to control the attachment pages’ behavior:

  • On existing sites, the option is set to 1 on upgrade, so that attachment pages continue to work as is.
  • For new sites, the option is set to 0 by default, which means attachment pages are redirected to the attachment URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org.
  • Sites administrators who want to enable or disable the attachment pages can set the option to 1 or 0, respectively.

When attachment pages are disabled, the adminadmin (and super admin) link “View attachment page” changes to “View Media File”.

Setting the option

Via WP CLICLI Command Line Interface. Terminal (Bash) in Mac, Command Prompt in Windows, or WP-CLI for WordPress.

To test this change or if you just want to change it on a live site, you can use WP CLI. You would do so like this:

wp option set wp_attachment_pages_enabled 0|1

Via a 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

Another option is to use this small plugin built by @costdev during the development process that allows enabling the attachment pages through an admin bar item.

Via the admin options page

While this is not usually recommended, you can visit wp-admin/options.php on your site, search for wp_attachment_pages_enabled and change the option right there.

TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #57913

Props to @joostdevalk for writing the dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase..

Props to @sabernhardt @bph and @webcommsat for peer review.

#6-4, #dev-notes, #dev-notes-6-4

Introducing admin notice functions in WordPress 6.4

Adminadmin (and super admin) notices are widely used within WordPress CoreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. and in the extender community. Admin notices have common markup patterns and CSSCSS Cascading Style Sheets. classes, but required maintaining HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. independently for each notice throughout a project.

In #57791, two new functions were proposed: wp_get_admin_notice() and wp_admin_notice().

These functions abstract the HTML markup generation to reduce the maintenance burden, encourage consistency, and enable argument and message filtering for all admin notices. In addition, a new wp_admin_notice action has been introduced, which fires before a notice is output.

New functions

wp_get_admin_notice()

  • Returns the markup for an admin notice.
  • Note: The markup is not fully escaped and care should be taken to select the appropriate escaping function before output.

wp_admin_notice()

  • Outputs the markup for an admin notice.
  • Markup is created using wp_get_admin_notice() and escaped using wp_kses_post() before output.

Parameters

Both functions have the following parameters:

  • string $message The message for the notice.
  • array $args An array of arguments for the notice.
    • string $type Optional. The type of admin notice. This will be appended to 'notice-' to create the HTML class name. For example, a type of 'success' will produce a 'notice-success' HTML class. Default empty string.
    • bool $dismissible Optional. Whether the notice is dismissible. Default false.
    • string $id Optional. The value for the HTML id attribute. Default empty string.
    • array $additional_classes Optional. An array of additional class names to use for the notice. These are used as provided. Default empty array.
    • array $attributes Optional. An associative array of HTML attributes for the notice. Boolean true attributes may just include the name of the attribute. Default empty array.
    • bool $paragraph_wrap Optional. Whether to wrap the message in <p></p> tags. Default true.

Filters

wp_get_admin_notice() applies the following filters:

  • wp_admin_notice_args – Filters the arguments for an admin notice.
    • Passed arguments: array $args, string $message
  • wp_admin_notice_markup – Filters the markup for an admin notice.
    • Passed arguments: string $markup, string $message, array $args

Actions

wp_admin_notice() fires the following action:

  • wp_admin_notice – Fires before an admin notice is output.
    • Passed arguments: string $message, array $args

Example usage

Output a dismissible success notice

wp_admin_notice(
  __( 'Plugin update failed.', 'my-text-domain' ),
  array(
    'type'               => 'error',
    'dismissible'        => true,
    'additional_classes' => array( 'inline', 'notice-alt' ),
    'attributes'         => array( 'data-slug' => 'plugin-slug' )
  )
);

Result

<div class="notice notice-error is-dismissible inline notice-alt" data-slug="plugin-slug"><p>Plugin update failed.</p></div>

Create a collection of notices to output at once 

$output = '';
foreach ( $success_messages as $message ) {
  $output .= wp_get_admin_notice(
    $message,
    array( 'type' => 'success' )
  );
}

echo wp_kses_post( $output );

Result

<div class="notice notice-success"><p>Success message 1</p></div>

(repeated for each notice)

Add a class to every ‘warning’ admin notice

add_filter( 'wp_admin_notice_args', 'myprefix_add_class_to_warnings' );
function myprefix_add_class_to_warnings( $args ) {
  if ( 'warning' === $args['type'] ) {
    $args['additional_classes'][] = 'my-class';
  }

  return $args;
}

Result

<div class="notice notice-warning my-class"><p>Warning message 1</p></div>

(repeated for each warning notice)

Usage in WordPress Core

The new admin notice functions have been implemented in most locations in WordPress Core. Further work will be done in WordPress 6.5 to complete the process and migrate older notices (using the 'updated' and 'error' classes) to the current admin notice pattern ('notice-success', 'notice-info', 'notice-warning', and 'notice-error').

Props to @joedolson and @webcommsat for peer review.

#6-4, #dev-notes, #dev-notes-6-4

Updates to user-interface components in WordPress 6.4

This post lists notable changes to the @wordpress/components package for the WordPress 6.4 release

Table of Contents

Making Popover.Slot optional

As part of a wider effort to streamline the developer experience of using 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/ as a platform/framework to build 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. editors, the Popover component has been tweaked so that it’s not necessary anymore to manually specify a Popover.Slot component (and a SlotFillProvider) somewhere in the ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org/. tree.

The Popover component now works out of the box, and the Popover.Slot component can be optionally used to tweak where the popover should render in the DOM tree.

A side-effect of this change is that some instances of Popover may not render inline anymore when a Popover.Slot is not rendered on the page, affecting both the popover’s DOM position and the styles inherited via the CSSCSS Cascading Style Sheets. cascade. To mitigate this use case, a new inline prop has been added to the Popover component, to force the component to render inline, thus preserving the legacy behavior.

For more information visit #53889, #53982, and #54912(top)

Rewriting Tooltip and TabPanel

The Tooltip and the TabPanel components have been completely rewritten to leverage third-party, headless libraries (specifically ariakit) as the first tasks of a new experimental effort within the package.

Both migrations were intentionally designed to avoid changes to 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. or developer experience, while also bringing several benefits:

  • better compliance with semantics, accessibilityAccessibility Accessibility (commonly shortened to a11y) refers to the design of products, devices, services, or environments for people with disabilities. The concept of accessible design ensures both “direct access” (i.e. unassisted) and “indirect access” meaning compatibility with a person’s assistive technology (for example, computer screen readers). (https://en.wikipedia.org/wiki/Accessibility) requirements, and related WAI-ARIA patterns;
  • better user experience thanks to using a widely used, well-tested underlying implementation;
  • better types and improved unit tests;
  • less maintenance required in the long term.

Specifically to the Tooltip component, the refactor also fixed a long-standing issue and presented the opportunity to align with other components already using a new placement prop in lieu of the legacy, now deprecated position prop.

For TabPanel, the only noteworthy change is that tabpanel elements now get a tabstop. This means that when focused on a Tab, pressing the [Tab] key will apply focus to the tabpanel itself, rather than jumping directly to the next focusable element within the tabpanel element.

As mentioned above, this was part of an experiment around using third-party libraries more deliberately in the components package. In the future, we may look at more opportunities for such rewrites, especially for components with more complex semantics and accessibility implementations.

For more information visit #48440, #54264, #54406, and #52133(top)

New props for the Modal component

The Modal component has been enhanced with a couple of additions to its APIs. Thanks to a new headerActions prop, developers using the Modal can inject buttons (and other elements) into the Modal‘s headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes., next to the close button.

The focusOnMount prop has also received an update, and it now accepts a new "firstContentElement" option. When setting focusOnMount="firstContentElement", the Modal component will try to focus on the first tabbable element within the Modal‘s content (ie. the markup passed via the children prop).

This is different from the pre-existing "firstElement" option, which causes focus to go to the first element anywhere within the Modal, including its header (usually the close button).

Note that it is the responsibility of the developer to ensure that, when focusOnMount="firstContentElement", there is at least one tabbable element within the Modal‘s children.

For more information visit #53328 and #54590(top)

Improving size consistency for UIUI User interface components

UI components across the editor (input fields, buttons, etc) are currently rendering in a range of heights between 30px and 40px. In order to add consistency and visual polish to the editor’s UI, we started working on standardizing components toward having a default height of 40px.

To ensure a smooth transition to the new default sizes, we have started to introduce a new, temporary __next40pxDefaultSize prop on selected components around the codebase, which will allow consumers to start opting into the new default size. Sometime after that, the temporary prop will be deprecated and ultimately removed, causing _all_ instances of the components to switch to the default 40px size out of the box.

To start opting into the new 40px default height, set the __next40pxDefaultSize prop to true:

<Button __next40pxDefaultSize>
  Code is poetry
</Button>

So far, the components exposing the new temporary __next40pxDefaultSize prop are:

For more information visit #46734 and #46741(top)

More granular control of decimal places on NumberControl-based components

A new spinFactor prop has been added to NumberControl, allowing consumers of the components to specify by how much should the input’s value increment/decrement with respect to the step. This is particularly useful when more granular control is needed (thus allowing for more decimal places and a smaller step) but without sacrificing the UXUX User experience around manually incrementing/decrementing the value.

Even if the prop was added to NumberControl, all components based on NumberControl can benefit from this change — this includes, for example, UnitControl and LineHeightControl.

For more information visit #52902(top)

Rendering CircularOptionPicker as a listbox by default

To improve CircularOptionPicker‘s semantics and keyboard navigation, the component has been tweaked to render and behave as a listbox by default. This change also causes the component to become a single tab stop, with the individual color options accessed using arrow keys.

In the (few) instances in which it makes sense for CircularOptionPicker to still render as a list of individual buttons, consumers of the component can use the asButtons prop to switch back to the legacy behavior.

For more information visit #52255 and #54290(top)

Adding an option for FormTokenField to create a new token when losing focus

A new tokenizeOnBlur prop has been added to FormTokenField, causing the component to tokenize its current input instead of discarding it when losing focus.

This is particularly useful when FormTokenField is used in places like modals, where the user may press a button causing the modal to close and FormTokenField to lose focus before its input could be tokenized.

For more information visit #54445(top)

Controlling open/closed state of Dropdown and DropdownMenu

The open/closed state of the Dropdown and DropdownMenu components can now be controlled by their consumers via the open, onToggle and defaultOpen props, allowing more flexibility and more advanced behaviors when using these components.

For more information visit #54257(top)

Props to @brookemk, @tyxla and @shireling for the help in writing these dev notesdev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase..

Props to @bph and @webcommsat for reviews

#6-4, #dev-notes, #dev-notes-6-4

New `registerInserterMediaCategory` API

From WordPress 6.4, extenders can register their own inserter media categories and provide users with more options from which to choose.

Even though the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.’s media categories are a 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. editor setting, it is a private one, which means it cannot be updated through the public updateSettings action. Extenders can only add new inserter media categories, and they don’t have any control over the core media categories, except for being able to disable the Openverse media categoryCategory The 'category' taxonomy lets you group posts / content together that share a common bond. Categories are pre-defined and broad ranging. with enableOpenverseMediaCategory block editor setting.

Every inserter media category should have a unique name property, which is used internally like an id, so it’s best to prefix this. Additionally, each category should have a unique label to be differentiated from the other ones(labels.name property).

You can read more about the used interfaces that a media category, media item and the request for media should abide to, on the documentation pages.

The example uses the Openverse category object and just updates the names used:

wp.data.dispatch( 'core/block-editor' ).registerInserterMediaCategory( {
	name: 'my-new-category',
	labels: {
		name: 'My new category',
		search_items: 'Search Openverse',
	},
	mediaType: 'image',
	async fetch( query = {} ) {
		const defaultArgs = {
			mature: false,
			excluded_source: 'flickr,inaturalist,wikimedia',
			license: 'pdm,cc0',
		};
		const finalQuery = { ...query, ...defaultArgs };
		// Sometimes you might need to map the supported request params 
                // according to `InserterMediaRequest`.
		// interface. In this example the `search` query param is named `q`.
		const mapFromInserterMediaRequest = {
			per_page: 'page_size',
			search: 'q',
		};
		const url = new URL( 'https://api.openverse.engineering/v1/images/' );
		Object.entries( finalQuery ).forEach( ( [ key, value ] ) => {
			const queryKey = mapFromInserterMediaRequest[ key ] || key;
			url.searchParams.set( queryKey, value );
		} );
		const response = await window.fetch( url, {
			headers: {
				'User-Agent': 'WordPress/inserter-media-fetch',
			},
		} );
		const jsonResponse = await response.json();
		const results = jsonResponse.results;
		return results.map( ( result ) => ( {
			...result,
			// If your response result includes an `id` prop 
                        // that you want to access later, it should
			// be mapped to `InserterMediaItem`'s `sourceId` prop. 
                        // This can be useful if you provide a report URL getter.
			// Additionally you should always clear the `id` value of 
                        // your response results because it is used to identify 
                        // WordPress media items.
			sourceId: result.id,
			id: undefined,
			caption: result.caption,
			previewUrl: result.thumbnail,
		} ) );
	},
	getReportUrl: ( { sourceId } ) =>
		`https://wordpress.org/openverse/image/${ sourceId }/report/`,
	isExternalResource: true,
} );

Props to @bph and @webcommsat for review.

#6-4, #dev-notes, #dev-notes-6-4

Introducing Block Hooks for dynamic blocks

WordPress 6.4 introduces 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. (#53987), a feature that provides an extensibility mechanism for Block Themes. This is the first step in emulating WordPress’ Hooks concept that allows developers to extend Classic Themes using filters and actions.

Specifically, the Block Hooks 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. allows a block to automatically insert itself relative to instances of other block types. For example, a “Like” button block can ask to be inserted before the Post Content block, or an eCommerce shopping cart block can ask to be inserted after the Navigation block.

Tenets and current limitations

There are two coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. tenets of Block Hooks:

  1. Front-end insertion should happen as soon as the 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 containing a hooked block is activated. In other words, the user isn’t required to insert the block in the Editor manually. Similarly, disabling the plugin should remove the hooked block from the front end.
  2. The user has the ultimate control over any automatically inserted blocks. This means that a hooked block is visible in the Editor, and the user’s decision to keep, remove, customize, or move the block will be respected and reflected on the front end.

To account for both tenets, tradeoffs had to be made. Block hooks are limited to templates, template parts, and patterns (i.e., the elements that define the layout of the theme). For patterns, this includes those provided by the theme, from Block Pattern Directory, or from calls to register_block_pattern.

Blocks cannot be hooked into post content or patterns crafted by the user, such as synced patterns or theme templates and template parts that the user has modified.

Furthermore, as of WordPress 6.4, you can’t automatically insert blocks that have a save function, or block validation errors will occur. In colloquial terms, this means that Block Hooks work with Dynamic blocks, not Static blocks. Refer to this article on the difference between the two.

Using Block Hooks

You can implement Block Hooks in two different ways: in a block’s block.json file or using the new 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.. While simpler, the block.json method provides a more limited implementation, so let’s review that first.

block.json

The block.json method allows you to hook a third-party block unconditionally, meaning that the block will be inserted relative to all instances of the target (anchor) block provided the abovementioned limitations.

In the block’s block.json file, include the blockHooks property. This property takes an object where the key (string) is the name of the block you want to hook into, and the value (string) specifies its position. Possible positions are:

  • before – inject before the target block.
  • after – inject after the target block.
  • firstChild – inject before the first inner block of the target container block.
  • lastChild – inject after the last inner block of the target container block.
{
    blockHooks: {
        'core/verse': 'before'
        'core/spacer': 'after',
        'core/column': 'firstChild',
        'core/comment-template': 'lastChild',
    }
}

In the example above, the block will be inserted before every Verse block that appears in an unmodified template, template part, or pattern. It will also be inserted after every Spacer block, etc.

When using the block.json method with firstChild or lastChild, a Setting SidebarSidebar A sidebar in WordPress is referred to a widget-ready area used by WordPress themes to display information that is not a part of the main content. It is not always a vertical column on the side. It can be a horizontal rectangle below or above the content area, footer, header, or any where in the theme. panel titled Plugins will be added to the target block in the Editor. This allows the user to toggle on and off the hooked block.

Below is an example of a Like button block that is hooked to the lastChild position in the Comment Template block.

The Like button block is hooked to the 'lastChild' position of the Core Comment Template block.

hooked_block_types

The hooked_block_types filter provides more flexibility. It allows you to hook any Dynamic block unconditionally, like the block.json method, or conditionally based on the template, template part, or pattern where the target (anchor) block is located.

The callback function for the filter accepts four parameters:

  • $hooked_blocks (array) – An array of hooked blocks.
  • $position (string) – The relative position of the hooked block: before, after, first_child, or last_child.
  • $anchor_block (string) – The name of the anchor block.
  • $context (WP_Block_Template|array) – The block template, template part, or pattern the anchor block belongs to.

Below are a few examples using the Like button block plugin (ockham/like-button) built to demonstrate Block Hooks functionality in a third-party block. The pattern example does require the Twenty Twenty-Four theme.

function example_block_hooks( $hooked_blocks, $position, $anchor_block, $context ) {

	// Template/Template Part hooks.
	if ( $context instanceof WP_Block_Template ) {
		
		// Hooks the "Like" button block before the Post Title in the Single template.
		if ( 
			'core/post-title' === $anchor_block &&
			'before' === $position &&
			'single' === $context->slug
		) {
			$hooked_blocks[] = 'ockham/like-button';
		}

		// Hooks the Login/Logout link block after the Navigation block if the context of the template part is a header.
		if ( 
			'core/group' === $anchor_block &&
			'last_child' === $position &&
			'header' === $context->area
		) {
			$hooked_blocks[] = 'core/loginout';
		}
	}

	// Pattern hooks.
	if ( is_array( $context ) && isset( $context['slug'] ) ) {
		
		// Hooks into the Post Meta pattern in the Twenty Twenty-Four theme.
		if ( 
			'core/post-terms' === $anchor_block && 
			'after' === $position && 
			'twentytwentyfour/post-meta' === $context['slug']
		) {
			$hooked_blocks[] = 'ockham/like-button';
		}
	}

	return $hooked_blocks;
}
add_filter( 'hooked_block_types', 'example_block_hooks', 10, 4 );

It’s important to note that $context will be an object of type WP_Block_Template for templates and template parts and an array for patterns. If you want to insert blocks conditionally using this parameter, make sure to check the parameter type before applying hooking blocks.

You will also note that you can only specify the name of the block that is being hooked. There is no way to set the attributes of the hooked block, so only the default instance of the block is inserted. Future improvements to Block Hooks will likely account for this limitation and others, providing a robust way for developers to extend Block Themes.

For more information on what additional features are currently being worked on, stay tuned to the tracking issue for Block Hook improvements.

Props to @bernhard-reiter, @gziolo, @webcommsat, and @bph for reviews.

#6-4, #dev-notes, #dev-notes-6-4