Real-Time Collaboration in the Block Editor

Real-time collaboration (RTC) in 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. editor allows multiple users to edit content simultaneously by utilizing Yjs.

This 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. covers three important aspects of the collaboration system that 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. and theme developers should be aware of:

  • How 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. boxes affect collaboration mode
  • The sync.providers 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. for customized sync transport
  • Common issues when building plugins that can run in a collaborative environment

Collaboration is disabled when meta boxes are present

The Problem

Classic WordPress meta boxes are not synced by the real-time collaboration system. To avoid data loss, collaboration is disabled when meta boxes are detected on a post.

Locked post modal when someone takes over a post
Locked post modal when trying to take over a post

What developers need to know

To allow collaboration, consider migrating meta box functionality to registered post meta with show_in_rest set to true, and use 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. plugins or block-based alternatives that read from WordPress data stores.

For example:

register_post_meta( 'post', 'example_subtitle', [
	'show_in_rest' => true, // Required for syncing.  
	'single' => true,  
	'type' => 'string',
	'revisions_enabled' => true, // Recommended to track via revision history.
] );

For more details on migrating from meta boxes, see the Meta Boxes guide in the Block Editor Handbook.


The sync.providers filter: Customizing the sync transport layer

Overview

The @wordpress/sync package uses a provider-based architecture for syncing collaborative editing data. By default, WordPress ships with an HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. polling provider. The sync.providers filter allows plugins to replace or extend the transport layer. For example, a plugin could switch from HTTP polling to WebSockets for lower-latency collaboration.


How it works

The filter is applied during provider initialization:

const filteredProviderCreators = applyFilters(
'sync.providers',
getDefaultProviderCreators() // array of provider creators
);

A provider creator is a function that accepts a ProviderCreatorOptions object (containing the Yjs ydoc, awareness, objectType, and objectId) and returns a ProviderCreatorResult with destroy and on methods. The destroy method is called when the provider is no longer needed, and the on method allows the editor to listen for connection status events (connecting, connected, disconnected).


Example: WebSocket provider

The following example replaces the default HTTP polling provider with a WebSocket-based transport using the y-websocket library:

import { addFilter } from '@wordpress/hooks';
import { WebsocketProvider } from 'y-websocket';

/**
 * Create a WebSocket provider that connects a Yjs document
 * to a WebSocket server for real-time syncing.
 */
function createWebSocketProvider( { awareness, objectType, objectId, ydoc } ) {
	const roomName = `${ objectType }-${ objectId ?? 'collection' }`;
	const serverUrl = 'wss://example.com/';

	const provider = new WebsocketProvider(
		serverUrl,
		roomName,
		ydoc,
		{ awareness }
	);

	return {
		destroy: () => {
			provider.destroy();
		},
		on: ( eventName, callback ) => {
			provider.on( eventName, callback );
		},
	};
}

addFilter( 'sync.providers', 'my-plugin/websocket-provider', () => {
	return [ createWebSocketProvider ];
} );


What developers need to know

  • The sync.providers filter is only applied when real-time collaboration is enabled.
  • Return an empty array to disable collaboration entirely.
  • Return a custom array to replace the default HTTP polling provider with your own transport (e.g., WebSockets, WebRTC).

Common issues when building plugins compatible with real-time collaboration

When real-time collaboration is active, all connected editors share the same underlying data state via Yjs. Plugins that interact with post data, especially custom post meta, need to follow certain patterns to avoid sync issues

Syncing custom post meta values

In addition to being registered, custom meta field UIUI User interface must be consumed from the WordPress data store and passed to controlled input components.
Always derive the input value directly from the WordPress data store via useSelect. In addition, use value instead of defaultValue on input components so the input always reflects the current data store state.

const metaValue = useSelect(
	select => select( 'core/editor' ).getEditedPostAttribute( 'meta' )?.example_subtitle,
	[]
);

<input
	value={ metaValue || '' }
	onChange={ event => {
		editPost( { meta: { example_subtitle: event.target.value } } );
	} }
/>

Avoiding local component state for shared data

When building a plugin UI that reads from the WordPress data store, avoid copying that data into local ReactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org state with useState. This applies to any shared data, such as post meta or block attributes. Doing so disconnects your component from the shared collaborative state: updates from other clients will update the store, but your component wonโ€™t reflect them after the initial render, leading to stale or conflicting data.

Blocks with side effects on insertion

Custom blocks that trigger side effects on insertion will trigger that side effect for all connected collaborators, since block content syncs immediately upon insertion.

For example, instead of auto-opening a modal when a block is inserted, show a placeholder with a button that opens the modal on click. This ensures side effects are intentional and local to the user taking the action.


Credits

Props @czarate, @alecgeatches, @maxschmeling, @paulkevan, and @shekharwagh for building real-time collaboration in the block editor alongside @ingeniumed, and for technical review and proofreading of this dev note.

Parts of this work are derived from contributions made by @dmonad inย this PR, and utilizes his Yjs library.

Props toย @wildworks and @tyxlaย for proofreading this dev note.

#dev-notes, #dev-notes-7-0, #7-0

Pseudo-element support for blocks and their variations in theme.json

WordPress 7.0 adds support for pseudo-class selectors (:hover, :focus, :focus-visible, and :active) directly on blocks and their style variations in theme.json. Previously, this was only possible for HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. elements like button and link under the styles.elements key. 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.-level interactive states could only be achieved through custom CSSCSS Cascading Style Sheets..

Variation-level pseudo-selectors

Block style variations can also define interactive states. This is particularly useful for variations like โ€œOutlineโ€ that have distinct visual styles requiring different hover behaviors:

{
    "styles": {
        "blocks": {
            "core/button": {
                "variations": {
                    "outline": {
                        "color": {
                            "background": "transparent",
                            "text": "currentColor"
                        },
                        ":hover": {
                            "color": {
                                "background": "currentColor",
                                "text": "white"
                            }
                        }
                    }
                }
            }
        }
    }
}

  • This is a theme.json-only 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.. There is no new UIUI User interface in Global Styles for these states in 7.0. Work on this is happening at #38277 and will be added in a future release.
  • The supported pseudo-selectors for core/button are: :hover, :focus, :focus-visible, and :active. Any others will be ignored.
  • Pseudo-selectors defined at the block level and at the variation level are independent โ€” you can define both without conflictconflict A conflict occurs when a patch changes code that was modified after the patch was created. These patches are considered stale, and will require a refresh of the changes before it can be applied, or the conflicts will need to be resolved..

See #64263 for more details

Props to @scruffian, @onemaggie for the implementation

Props to @mikachan, @scruffian for technical review and proofreading.

#7-0, #dev-notes, #dev-notes-7-0

Breadcrumb block filters

WordPress 7.0 introduces a new Breadcrumbs 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. that can be placed once โ€” such as in a themeโ€™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. โ€” and automatically reflects the siteโ€™s navigation hierarchy.

Breadcrumbs block in aย header template partย using Twenty Twenty-Five theme, here showing the trail for a child page

Two filters provide developers with control over the breadcrumb trail output.

block_core_breadcrumbs_items

This 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. allows developers to modify, add, or remove items from the final breadcrumb trail just before rendering. Each item is an array with three properties:

  1. label (string) โ€” the breadcrumb text.
  2. an optional url (string) โ€” the breadcrumb link URLURL A specific web address of a website or web page on the Internet, such as a websiteโ€™s URL www.wordpress.org.
  3. an optional allow_html (bool) โ€” whether to allow HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. in the label. When true, the label will be sanitized with wp_kses_post(), allowing only safe HTML tags. When false or omitted, all HTML will be escaped with esc_html().

Example: Prepend a custom breadcrumb item

  add_filter( 'block_core_breadcrumbs_items', function ( $breadcrumb_items ) {
        array_unshift( $breadcrumb_items, array(
                'label' => __( 'Shop', 'myplugin' ),
                'url'   => home_url( '/shop/' ),
        ) );

        return $breadcrumb_items;
  } );

block_core_breadcrumbs_post_type_settings

When a post type has multiple taxonomies or when a post is assigned to multiple terms within a taxonomyTaxonomy A taxonomy is a way to group things together. In WordPress, some common taxonomies are category, link, tag, or post format. https://codex.wordpress.org/Taxonomies#Default_Taxonomies., there could be numerous ways to construct the breadcrumbs trail. For example in a post that has both categories and tags a user might want to show in the breadcrumbs trail the categories (default), the tags and/or select a specific 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.).

This filter controls which taxonomy and terms appear in the Breadcrumbs block trail for posts that use taxonomy-based breadcrumbs. It applies to non-hierarchical post types (e.g., posts, products) or hierarchical post types when the blockโ€™s โ€œPrefer taxonomy termsโ€ setting is enabled (under advanced settings). It does not affect hierarchical ancestor-based trails (e.g., parent/child pages).

The filter receives three parameters:

  • $settings (array) โ€” an empty array by default. Callbacks should populate the array and return it with the following optional keys:
    • taxonomy (string) โ€” taxonomy slug to use for breadcrumbs.
    • term (string) โ€” term slug to prefer when the post has multiple terms in the selected taxonomy.
  • $post_type (string) โ€” the post type slug.
  • $post_id (int) โ€” the post ID, enabling per-post customization.

Fallback behavior

  • If the preferred taxonomy doesnโ€™t exist or has no terms assigned, fall back to the first available taxonomy with terms assigned.
  • If the preferred term doesnโ€™t exist or isnโ€™t assigned to the post, fall back to the first term
  • If the post has only one term, that term is used regardless of setting

Example 1: Set a preferred taxonomy and term per post type

add_filter( 'block_core_breadcrumbs_post_type_settings', function( $settings, $post_type ) {
	if ( $post_type === 'post' ) {
		$settings['taxonomy'] = 'category';
		$settings['term'] = 'news';
	}
	if ( $post_type === 'product' ) {
		$settings['taxonomy'] = 'product_tag';
	}
	return $settings;
}, 10, 2 );

Example 2: Choose a specific term per post

add_filter( 'block_core_breadcrumbs_post_type_settings', function ( $settings, $post_type, $post_id ) {
	if ( $post_type !== 'post' ) {
		return $settings;
	}
	
	$terms = get_the_terms( $post_id, 'category' );

	if ( $terms ) {
		$settings['taxonomy'] = 'category';
		$settings['term']     = end( $terms )->slug;
	}

	return $settings;
}, 10, 3 );

See 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/ pull requests: 74169, 73283, 74170.

Props to @karolmanijak, @ntsekouras for the implementation.

Props to @karolmanijak for technical review.

Props to @mcsf for copy review.

Login to Reply<\/a><\/li><\/ul><\/div>","commentTrashedActions":"

Customizable Navigation Overlays in WordPress 7.0

WordPress 7.0 introduces Customizable Navigation Overlays, giving site owners full control over their mobile navigation menus using 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. editor.

Previously, when a visitor tapped a hamburger menu icon on a mobile device, WordPress displayed a fixed default overlay with no support for customization. The design, layout, and content were locked.

Customizable Navigation Overlays remove this restriction entirely โ€” any overlay can now be built from blocks and patterns in the Site Editor. This includes a dedicatedย Navigation Overlay Closeย block for placing and styling a close button anywhere within the overlay.

How overlays work

Navigation overlays are implemented as template parts using a newย navigation-overlayย template part area, managed principally through the Navigation blockโ€™s overlay controls in the Site Editor. Because they are template parts, they can also be found and edited via theย Patternsย section in the Site Editor 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.. Each overlay is assigned to a Navigation block โ€” while the same overlay can be referenced by more than one, a one-to-one relationship is the most common pattern.

What goes inside an overlay is entirely up to the author. As a standard block canvas, it can contain any block โ€” navigation, social icons, a search field, a site logo, calls to actionโ€ฆor any combination! A Navigation block is the typical inclusion but is not a requirement. Because overlays only function correctly when rendered by a Navigation block, overlay template parts are intentionally excluded from the general block inserter โ€” this prevents them from being inserted accidentally into other parts of a template.

The feature is opt-in: by default, the Navigation block continues to use the standard overlay behaviour from previous versions of WordPress. A custom overlay can be activated in three ways:

  • Creating a new overlayย โ€” via theย Overlaysย section in the Navigation blockโ€™s sidebar controls in the Site Editor
  • Selecting an existing overlayย โ€” from the same controls, choosing from any overlays already created or bundled with the active theme
  • Theme pre-assignmentย โ€” a theme can reference a bundled overlay directly in the Navigation block markup (covered in the developer section below)

For theme developers: bundling overlays with your theme

Themes can ship pre-built navigation overlays so they are available as soon as the theme is activated. The recommended approach is to provide both a default overlay template part and a set of overlay patterns.

Template parts vs patterns

Understanding the distinction helps decide how to structure an overlay offering:

  • Aย template partย is the overlay itself โ€” the component that gets rendered when a Navigation block uses an overlay. Shipping a template part means a ready-to-use overlay is available from the moment the theme is activated.
  • Patternsย are design options that appear in theย Designย tab when editing a navigation overlay in the Site Editor. Selecting a pattern replaces the current overlay content with the patternโ€™s block markup, letting users switch between distinct designs.

A patterns-only approach is also valid โ€” useful when a theme wants to offer design options without pre-applying an overlay automatically. In this case, users create a new overlay via the Navigation blockโ€™s controls and pick from the themeโ€™s patterns as a starting point.

Updating your Theme

1. Register the template part inย theme.json

Registering the template part inย theme.jsonย is required. Without it, the template part is assigned theย uncategorizedย area and will not be recognized by the Navigation block as an overlay.

Add an entry to theย templatePartsย array, settingย areaย toย navigation-overlay:

{
    "templateParts": [
        {
            "area": "navigation-overlay",
            "name": "my-custom-overlay",
            "title": "My Custom Overlay"
        }
    ]
}

2. Create the template part file

Create the corresponding HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. file in the themeโ€™sย parts/ย directory. The filename should match theย nameย value fromย theme.json.

It is strongly recommended to include the Navigation Overlay Close block within the overlay. If it is omitted, WordPress will automatically insert a fallback close button on the frontend for 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) and usability reasons โ€” but that button may not match the overlayโ€™s design or be positioned as expected. Including it explicitly gives full control over its appearance and placement.

<!-- parts/my-custom-overlay.html -->
<!-- wp:group {"layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group">
    <!-- wp:navigation-overlay-close /-->
    <!-- wp:navigation {"layout":{"type":"flex","orientation":"vertical"}} /-->
</div>
<!-- /wp:group -->

3. Register overlay patterns

Overlay patterns are registered usingย register_block_pattern(). Settingย blockTypesย toย core/template-part/navigation-overlayย scopes the pattern so it only appears when editing a navigation overlay template part โ€” not in the general inserter.

register_block_pattern(
    'my-theme/navigation-overlay-default',
    array(
        'title'      => __( 'Default Overlay', 'my-theme' ),
        'categories' => array( 'navigation' ),
        'blockTypes' => array( 'core/template-part/navigation-overlay' ),
        'content'    => '<!-- wp:group {"layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group">
    <!-- wp:navigation-overlay-close /-->
    <!-- wp:navigation {"layout":{"type":"flex","orientation":"vertical"}} /-->
</div>
<!-- /wp:group -->',
    )
);

4. Pre-configuring the Navigation block (optional)

A theme can optionally pre-configure a Navigation block to reference a specific overlay by setting theย overlayย attribute in the block markup. The value should be the template part slug only โ€” without a theme prefix:

<!-- wp:navigation {"overlay":"my-custom-overlay"} /-->

Using the slug only โ€” without a theme prefix โ€” is important for future compatibility: WordPress plans to allow template parts to persist across theme switches, and a theme-prefixed identifier would break that. This follows the same convention asย headerย andย footerย template parts.

Theย overlayย attribute is entirely optional โ€” users can select or change the overlay at any time using the Navigation blockโ€™s sidebar controls.

Known limitations

Template parts and theme switching

Navigation overlay template parts are currently tied to the active theme. Custom overlays will not be preserved if the active theme is switched. This is a known limitation tracked inย gutenberg#72452.

Overlays are full-screen only

In this initial release, navigation overlays are always rendered full-screen. Non-full-screen overlay styles (such as a sidebar drawer) are not yet supported. This requires implementing overlays as a trueย <dialog>ย element โ€” including support for clicking outside to close โ€” which is planned for a future release.

Not a generic popup or dialog

Navigation Overlays are intentionally scoped to the Navigation block and are not designed as a general-purpose popup or dialog implementation. For broader use cases โ€” such as modal dialogs triggered by arbitrary content โ€” a dedicated Dialog block is in development and tracked inย gutenberg#61297.

Questions and feedback

Until now, the mobile navigation overlay has been one of the few remaining areas of a block theme that couldnโ€™t be designed in the Site Editor. Navigation Overlays change that. An overlay can contain anything blocks can express โ€” a simple menu with a styled close button, a full-screen layout with the site logo and a call to action, or a content-rich experience that turns the mobile menu into a destination in its own right.

There is a lot of creative space here, and seeing what the community builds with it will be exciting.

Questions are welcome in the comments below.

Further reading


Props @onemaggie for implementation contributions and technical review, @mikachan, @jeryj @scruffian for proofreading, and @mmcalister, whoseย Ollie Menu Designerย 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. helped validate community demand for this functionality.

#7-0, #dev-notes, #dev-notes-7-0, #navigation

DataViews, DataForm, et al. in WordPress 7.0

Previous cycle: WordPress 6.9.

This is a summary of the changes introduced in the โ€œdataviews spaceโ€ during the WordPress 7.0 cycle from 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. perspective. They have been posted inย the corresponding iteration issueย as well. To follow whatโ€™s next, subscribe to theย iteration issue for WordPress 7.1.

The changes listed here includeย 166 contributionsย byย 35 unique authorsย across the community during the pastย 4.5 monthsย (since October 17th, 2025).

Login to Reply<\/a><\/li><\/ul><\/div>","commentTrashedActions":"

PHP-only block registration

Developers can now create simple blocks using only PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher. This is meant for blocks that only need server-side rendering and arenโ€™t meant to be highly interactive. It isnโ€™t meant to replace the existing client-side paradigm, nor is it meant to ever be as featureful! However, 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. could help developers avoid extra complexity and could thus foster 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. adoption, especially in classic themes or server-driven workflows.

To use it, call register_block_type with the new autoRegister flag. Note that a render_callback function must also be provided:

function gutenberg_register_php_only_blocks() {
    register_block_type(
        'my-plugin/example',
        array(
            'title'           => __( 'My Example Block', 'myplugin' ),
            'attributes'      => array(
                'title'   => array(
                    'label'   => __( 'Title', 'myplugin' ),
                    'type'    => 'string',
                    'default' => 'Hello World',
                ),
                'count'   => array(
                    'label'   => __( 'Count', 'myplugin' ),
                    'type'    => 'integer',
                    'default' => 5,
                ),
                'enabled' => array(
                    'label'   => __( 'Enabled?', 'myplugin' ),
                    'type'    => 'boolean',
                    'default' => true,
                ),
                'size'    => array(
                    'label'   => __( 'Size', 'myplugin' ),
                    'type'    => 'string',
                    'enum'    => array( 'small', 'medium', 'large' ),
                    'default' => 'medium',
                ),
            ),
            'render_callback' => function ( $attributes ) {
                return sprintf(
                    __( '<p>%s: %d items (%s)</p>', 'myplugin' ),
                    esc_html( $attributes['title'] ),
                    $attributes['count'],
                    $attributes['size']
                );
            },
            'supports'        => array(
                'autoRegister' => true,
            ),
        )
    );
}

add_action( 'init', 'gutenberg_register_php_only_blocks' );

These blocks will automatically appear in the editor without requiring any 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 registration, and, wherever possible, the editor will automatically generate controls in the Inspector Controls 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. to allow users to edit block attributes:

Note that controls will not be generated for attributes with the local role or for attributes whose types are not supported.

See #64639 for more details.

Props to @priethor for the implementation.
Props to @wildworks for reviewing this 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..

Login to Reply<\/a><\/li><\/ul><\/div>","commentTrashedActions":"

Iframed Editor Changes in WordPress 7.0

Previous posts on this topic:

Current Situation

  • All editors except the post editor (site editor, template editor, all 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., template and device previews) are currently already iframed unconditionally.
  • The post editor is currently only iframed in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. if all registered blocks (across all plugins) use block 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. version 3 (or higher).
  • When 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/ is active with a block-based theme, the post editor is forced to be iframed regardless of block API versions used.

Whatโ€™s Changing in WordPress 7.0 (#75187)

Instead of checking the registered blocks across all plugins, only the block API versions of blocks that are actually inserted in the post will now be checked. If all blocks inserted are version 3 or higher, the post editor will be iframed. If not, the iframeiframe iFrame is an acronym for an inline frame. An iFrame is used inside a webpage to load another HTML document and render it. This HTML document may also contain JavaScript and/or CSS which is loaded at the time when iframe tag is parsed by the userโ€™s browser. will be removed to ensure the lower-versioned blocks are guaranteed to work. Block authors are encouraged to upgrade their blocks to version 3.

Enforcement in the Gutenberg 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. (#75475)

While the iframe is already enforced for block-based themes, it will now also enforced for classic themes from Gutenberg plugin version 22.6. (Note that the post editor may already have been iframed with classic themes if all registered blocks met the required block API version.) Most blocks that are version 2 and lower are expected to work fine, and the enforcement in the plugin is to gather feedback on specific cases where blocks might break before attempting to roll out iframe enforcement in future versions of WordPress. Please comment below if you are affected and the team will help with a solution.

Please note that the iframe is NOT enforced in WordPress 7.0! The timeline to enforce it has been revised in favor of a more gradual rollout to allow more time and feedback.

Thank you to @mamaduka, @wildworks and @mcsf for reviewing.

Login to Reply<\/a><\/li><\/ul><\/div>","commentTrashedActions":"

Changes to the Interactivity API in WordPress 7.0

New watch() function

WordPress 7.0 adds a watch() function to the @wordpress/interactivity package. This brand-new function subscribes to changes in any reactive value accessed inside a callbackโ€”and reruns it anytime those values change.

Now, you can tie the Interactivity 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.โ€™s data-wp-watch to a DOM elementโ€™s lifecycle and use that for reacting to state changes. But how would a process observe those changes programmatically, independently of the DOM? How could you set something up to run side effects at the store level, set up logging, or synchronize state between stores? The watch() function fills this gap.

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

const { state } = store( 'myPlugin', {
    state: {
        counter: 0,
    },
} );

// Runs immediately and re-runs whenever `state.counter` changes.
watch( () => {
    console.log( 'Counter is ' + state.counter );
} );

The function returns an unwatch callback that stops the watcher:

const unwatch = watch( () => {
    console.log( 'Counter is ' + state.counter );
} );

// Later, to stop watching:
unwatch();

The callback can also return a cleanup function that runs before each re-execution and when unwatch() disposes of the watcher:

const unwatch = watch( () => {
    const handler = () => { /* ... */ };
    document.addEventListener( 'click', handler );

    return () => {
        document.removeEventListener( 'click', handler );
    };
} );

See #75563 for more details.

Props to @luisherranz for the implementation.

Deprecated state.navigation properties in core/router

The state.navigation.hasStarted and state.navigation.hasFinished properties in the core/router store were always intended as internal implementation details. Used for the loading bar animation, they were never intended to be part of the public API.

Starting in WordPress 7.0, accessing state.navigation from the core/router store is deprecated and will trigger a console warning in development mode (SCRIPT_DEBUG). Plus, in a future version of WordPress, direct access will not work at all. WordPress 7.1 will add an official mechanism for tracking navigation states.

See #70882 for more details.

Props to @yashjawale for the implementation.

state.url from core/router is now populated on the server

Previously, you initialized state.url in the core/router store on the client by setting it to window.location.href. That meant the value was undefined until the @wordpress/interactivity-router module finished loading asynchronously, and you had to guard against that initial undefined state and 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. out the subsequent initialization. Or else things would reactReact React is a JavaScript library that makes it easy to reason about, construct, and maintain stateless and stateful user interfaces. https://reactjs.org to it as an actual navigation.

Starting in WordPress 7.0, this value is populated on the server during directive processing. So its value doesnโ€™t change until the first client-side navigation occurs.

And that means you can combine watch() and state.url to reliably track client-side navigations, for example, to send analytics on every virtual page view:

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

const { state } = store( 'core/router' );

watch( () => {
    // This runs on every client-side navigation.
    sendAnalyticsPageView( state.url );
} );

See #10944 for more details.

Props to @luisherranz for the implementation.

#7-0, #dev-notes, #dev-notes-7-0, #interactivity-api

Dropping support for PHP 7.2 and 7.3

Support for PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher 7.2 and 7.3 will be dropped in WordPress 7.0, currently scheduled for release in April 2026. The minimum recommended version of PHP will remain at 8.3, but the new minimum supported version of PHP will be 7.4.0.

The minimum supported version of PHP was last raised in July 2024 to 7.2.24. Since then usage of PHP 7.2 and 7.3 has dropped below 4% of monitored WordPress installations.

Historically, the project has used 5% as the baseline usage percentage that a PHP version must fall below before it can be considered for a well-earned retirement. Now that usage of PHP 7.2 and 7.3 combined has fallen well below that, the process to increase the minimum supported PHP version can proceed.

The goal of increasing the minimum supported version of PHP is to ensure the long-term maintainability of WordPress. The benefits to increasing the minimum supported PHP version manifest over time across multiple areas, including 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. and theme ecosystem, tooling and libraries for AI, the long-term perception of the WordPress project, developer relations, and eventually within the WordPress codebase itself, including its developer tooling and automated testing infrastructure.

Discussion around this minimum version bump can be found here on the Trac ticket.

What about PHP 8 and higher?

WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. is fully compatible with PHP 8.0 to 8.3 and is beta compatible with PHP 8.4 and 8.5.

A full breakdown of which PHP versions are compatible with each version of WordPress can be found in the WordPress Core Handbook..

What about security support?

Sites that are running PHP 7.2 or 7.3 will remain on the 6.9 branchbranch A directory in Subversion. WordPress uses branches to store the latest development code for each major release (3.9, 4.0, etc.). Branches are then updated with code for any minor releases of that branch. Sometimes, a major version of WordPress and its minor versions are collectively referred to as a "branch", such as "the 4.0 branch". of WordPress once 7.0 is released. While only one branch officially receives security updates, fixes are backported down to WordPress 4.7 as a courtesy when possible.

What about the 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/ plugin?

The minimum supported version of PHP will also be increased to 7.4 in the Gutenberg plugin.

Going forward

There are no plans to bump the minimum supported PHP version on a set schedule. The Core team will continue to monitor PHP version usage and work with the Hosting team to encourage users and hosting companies to upgrade their versions of PHP as swiftly as possible. The 5% usage threshold will continue to be used as the standard for the foreseeable future.

At the time of publishing, the PHP version usage is as follows:

  • 8.5: 0.23%
  • 8.4: 4.90%
  • 8.3: 16.74%
  • 8.2: 27.29%
  • 8.1: 15.39%
  • 8.0: 5.69%
  • 7.4: 22.20%
  • 7.3: 2.04%
  • 7.2: 1.81%

Update PHP today

If you need more information about PHP or how to update it,ย check out this support article that explains more, guides you through the process, and provides instructions for contacting your web hosting provider for assistance.

Props to all those that have contributed to this discussion recently. Thanks to @desrosj, @westonruter, and @jorbin for feedback and proof-reading this post.

Login to Reply<\/a><\/li><\/ul><\/div>","commentTrashedActions":"

Adjacent Post Navigation Changes in WordPress 6.9 and Compatibility Issues

TL;DR

WordPress 6.9 introduced a fix for adjacent post navigation when posts have identical publication dates. While this resolved a long-standing bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority., it inadvertently caused infinite loops in some extensions that modify the get_adjacent_post() WHERE clause.

What Changed in WordPress 6.9

In WordPress 6.9 (Trac #8107), a bug fix landed where next/previous post navigation failed when multiple posts shared identical post_date values. This commonly occurred when bulk-publishing draft posts.

The Technical Change

The get_adjacent_post() functionโ€™s WHERE clause was modified to include ID-based comparison as a tiebreaker:

Before (WordPress 6.8 and earlier):

WHERE p.post_date > '2024-01-01 12:00:00' 
  AND p.post_type = 'post'

After (WordPress 6.9):

WHERE (
    p.post_date > '2024-01-01 12:00:00' 
    OR (
      p.post_date = '2024-01-01 12:00:00' 
      AND p.ID > 123
    )
  ) 
  AND p.post_type = 'post'

This ensures deterministic ordering when posts have identical dates, using the post ID as a secondary sort criterion.

Additionally, the ORDER BY clause was updated:

-- Before
ORDER BY p.post_date DESC 
LIMIT 1

-- After  
ORDER BY p.post_date DESC, 
         p.ID DESC 
LIMIT 1

The Problem: Infinite Loops in Some Themes/Plugins

As Trac ticket #64390 documents, some plugins and themes modify adjacent post navigation to change behavior. For example, WooCommerceโ€™s Storefront theme navigates between products instead of regular posts. These plugins use the get_{$adjacent}_post_where 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 replace the current postโ€™s date with a different postโ€™s date.

Hereโ€™s what was happening:

  1. 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. 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. into get_previous_post_where filter.
  2. Plugin does string replacement: replaces $current_post->post_date with $target_product->post_date.
  3. Problem: The new WHERE clause structure includes the post date in TWO places (date comparison AND ID comparison).
  4. Simple string replacement modified the date comparison but left the ID comparison unchanged.
  5. Query returns the same post repeatedly โ†’ infinite loopLoop The Loop is PHP code used by WordPress to display posts. Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post. https://codex.wordpress.org/The_Loop.

Real-World Example: Storefront Theme

The fix to the WooCommerce Storefront theme illustrates this issue. They had to add special handling for the ID comparison:

// Replace the post date (works as before)
$where = str_replace( $post->post_date, $new->post_date, $where );

// NEW: Also need to replace the ID comparison (WordPress 6.9+)
if ( strpos( $where, 'AND p.ID ' ) !== false ) {
    $search = sprintf( 'AND p.ID %s ', $this->previous ? '<' : '>' );
    $target = $search . $post->ID;
    $replace = $search . $new->ID;
    $where = str_replace( $target, $replace, $where );
}

For Plugin Developers: Detecting and Fixing the Issue

How to Detect If Your Plugin Is Affected

Your plugin is likely affected if it:

  1. Uses the get_{$adjacent}_post_where filter.
  2. Performs string replacement on post dates in the WHERE clause.
  3. Changes which post is considered โ€œadjacentโ€ (like navigating between custom post types instead).

To test if your plugin works correctly with WordPress 6.9:

  1. Create 3-4 posts with identical publication dates (bulk publish drafts).
  2. Navigate between them using your pluginโ€™s adjacent post functionality.
  3. Verify that navigation moves to different posts (not the same post repeatedly).
  4. Check for infinite loops or performance issues.

Quick Fix: Handle the ID Comparison

If youโ€™re doing date replacement in the WHERE clause, you also need to handle the ID comparison. There are numerous ways to tie a knot, but the following example is loosely inspired by Storefrontโ€™s recent fix.

add_filter( 'get_next_post_where', 'example_custom_adjacent_post_where', 10, 5 );
add_filter( 'get_previous_post_where', 'example_custom_adjacent_post_where', 10, 5 );

function example_custom_adjacent_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {

    // IMPORTANT: Replace this with your logic to find the desired adjacent post.
    $adjacent_post = example_find_adjacent_post_function( $post );
	
    if ( $adjacent_post instanceof WP_Post ) {
        // Replace the date comparison.
        $where = str_replace( $post->post_date, $adjacent_post->post_date, $where );

        // Replace the post ID in the comparison.
        $where = preg_replace(
            "/AND p\.ID (<|>) {$post->ID}\)/",
            "AND p.ID $1 {$adjacent_post->ID})",
            $where
        );
    }

    return $where;
}

You could also add a version_compare( $wp_version, '6.9', '>=' ) test if youโ€™d like to support multiple versions.

Important: donโ€™t forget to first test any code on your site, and customize it according to your needs.

Next time

At the time, this was not a known breaking change, and was classed as a bug fix.

However as @jmdodd points out in the ticket, changes to WP_Query, especially SQL changes should be, by default, communicated more widely. Going forward:

  1. Reach out to known plugins using the get_{$adjacent}_post_where filter.
  2. Include a migrationMigration Moving the code, database and media files for a website site from one server to another. Most typically done when changing hosting companies. guidance in the 6.9 field guideField guide The field guide is a type of blogpost published on Make/Core during the release candidate phase of the WordPress release cycle. The field guide generally lists all the dev notes published during the beta cycle. This guide is linked in the about page of the corresponding version of WordPress, in the release post and in the HelpHub version page. (as applicable).
  3. Test against any known, popular plugins that modify adjacent post queries.

Whatโ€™s Next and Getting Help

Discussions have started on the ticket about ways to make this more robust in future WordPress versions. If youโ€™re experiencing issues related to this change:

  1. Check Trac ticket #64390 for the latest updates.
  2. Ask questions in the #core channel on WordPress SlackSlack Slack is a Collaborative Group Chat Platform https://slack.com/. The WordPress community has its own Slack Channel at https://make.wordpress.org/chat/.
  3. Review the original fix PR #10394.

Thank you for your patience and understanding. If you maintain a plugin affected by this change, please update it using the guidance above, and donโ€™t hesitate to reach out if you need assistance.

Thanks to @westonruter @isabel_brison @andrewserong @jmdodd for helping to prepare this post.

#6-9, #dev-notes, #dev-notes-6-9