WordPress 7.0 Interactivity API gets a watch() function

WordPress 7.0 lands soon, and it brings a few refinements to the WordPress 7.0 Interactivity API that anyone building reactive blocks has been waiting for. The directive-based approach handles DOM-heavy interactions well. It has always been awkward when the logic you needed to run had nothing to do with a particular element.

I have lost afternoons to syncing state between two stores and ending up with a pile of data-wp-watch attributes whose only job was to fire one side effect. The new watch() function closes that gap, and it makes the whole thing easier to keep stable.

The programmatic watch() function

Reacting to a state change used to mean tying yourself to the lifecycle of a DOM element through data-wp-watch. Fine for UI updates, bad for logging, analytics, or keeping two stores in sync. The WordPress 7.0 Interactivity API gives you a programmatic watch() in the @wordpress/interactivity package instead.

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

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

// This runs immediately and re-runs whenever state.counter changes.
const unwatch = watch( () => {
    console.log( 'Counter is currently: ' + state.counter );
    
    // You can also return a cleanup function
    return () => {
        console.log( 'Cleaning up before next run...' );
    };
} );

// When you're done, just call the returned function to stop the watcher
// unwatch();

That beats attaching an invisible div so a directive has somewhere to live. I traced how the API got to this point in Interactivity API: Extending Core Blocks Without the Bloat.

Fixing the state.url race condition

state.url on initial page load was one of the sharper edges in earlier versions. The router module loaded asynchronously, so state.url sat at undefined for a split second before snapping to window.location.href. Everyone ended up writing guard code so that first initialization would not be read as a fresh navigation.

In WordPress 7.0 the server populates state.url, so the value is there the moment the client script initializes. Put watch() next to it and tracking virtual page views takes a few lines:

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

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

watch( () => {
    // This reliably runs on every client-side navigation, 
    // without the initial "undefined" headache.
    bbioon_send_to_analytics( state.url );
} );

Deprecations in core/router

Some house cleaning came along with it. state.navigation.hasStarted and state.navigation.hasFinished in core/router are now deprecated. They were only ever internal details of the loading bar, though plenty of us reached for them anyway because nothing better existed.

If you use them, you will start seeing console warnings in SCRIPT_DEBUG mode. A formal navigation state mechanism is expected in WordPress 7.1, so until then move that logic onto the server-populated state.url and watch() above. I went through the transition side of this in smooth client navigation with Interactivity API.

If the WordPress 7.0 Interactivity API is eating your dev hours, hand it to me. I have been wrestling with WordPress since the 4.x days.

What to check before you upgrade

The WordPress 7.0 Interactivity API is moving away from DOM dependency and toward something you can reason about on its own. watch() plus the improved server-side state removes a whole class of race conditions and keeps your front-end logic in one place. Audit your router store usage before those deprecations become breaking changes.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.