WordPress 6.9 Features: What Developers Need to Know

WordPress 6.9 continues the Gutenberg Phase 3 roadmap with collaborative editing foundations, expanded block-locking controls, and a redesigned template part focus mode. For plugin and theme developers the most impactful changes are: the new wp_block_supports() API for registering custom block support flags, improved WP_HTML_Tag_Processor with bookmark support, and interoperability improvements to the Interactivity API store.

Problem: A WordPress site is running 6.8 and the release notes for 6.9 list broad improvements — it is unclear which changes require plugin or theme code updates versus which are automatic engine improvements.

Solution: WordPress 6.9 focuses on the Gutenberg merge phase: improved block locking UI, a stabilised Interactivity API store API, and the wp_admin_notice() function becoming the standard for admin notices. Review any plugin that uses admin_notices directly and migrate to wp_admin_notice(). Test block themes for compatibility with updated block templates shipped in core.


The code below demonstrates the new block support registration API, the bookmark API on WP_HTML_Tag_Processor for multi-pass HTML mutation, and the updated Interactivity API getServerState() helper introduced in 6.9.


 __( 'Lazy Load', 'my-plugin' ),
            'apply_to_blocks' => [ 'core/image', 'core/cover', 'my-plugin/hero' ],
        ]
    );
} );

// Read the support flag in render_callback or a block filter:
add_filter( 'render_block', function ( string $block_content, array $block ): string {
    $supports_lazy = wp_get_block_support( $block['blockName'], 'my-plugin/lazy-load' );
    if ( $supports_lazy && isset( $block['attrs']['myPluginLazyLoad'] ) && $block['attrs']['myPluginLazyLoad'] ) {
        // Add loading="lazy" to any img tags in the block output
        $block_content = str_replace( 'next_tag( 'img' ) ) {
    $p->set_bookmark( 'first-img' );
    $p->set_attribute( 'alt', 'Card image' );
    break;
}

// Second pass: jump back to the bookmarked position
$p->seek( 'first-img' );
$p->set_attribute( 'loading', 'lazy' );

echo $p->get_updated_html();
// 
Card image

Hello

// ── 3. Interactivity API: getServerState() (WordPress 6.9) ──────────────── // PHP: pass server-rendered state flag wp_interactivity_state( 'my-namespace', [ 'isLoggedIn' => is_user_logged_in(), 'postId' => get_the_ID(), ] );


// view.js — Interactivity API: access server state
import { store, getServerState } from '@wordpress/interactivity';

store( 'my-namespace', {
    state: {
        // getServerState() returns the PHP-provided initial state (read-only)
        get isLoggedIn() {
            return getServerState().isLoggedIn;
        },
    },
    actions: {
        handleClick() {
            const { isLoggedIn } = getServerState();
            if ( ! isLoggedIn ) {
                window.location.href = '/wp-login.php';
            }
        },
    },
} );


NOTE: WP_HTML_Tag_Processor bookmarks are invalidated if any mutation changes the byte offset before the bookmark position — always set bookmarks in a read-only first pass, then apply mutations in reverse document order (last bookmark first) or use a fresh processor instance for each mutation round to avoid stale bookmark offsets.