Generated banner for Wp Interactivity API
Generated logo for Wp Interactivity API

Wp Interactivity API

WordPressOfficialPublished by WordPress

  • Skill
  • Tool integration
  • Software development
  • GPL-2.0-or-later
  • No issues found

From the author

Use when building or debugging WordPress Interactivity API features (data-wp-* directives, @wordpress/interactivity store/state/actions, block viewScriptModule integration, wp_interactivity_*()) including performance, hydration, and directive behavior.
— the description by WordPress

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to working with the WordPress Interactivity API. It covers the implementation of data-wp-* directives, store management, and server-side rendering best practices to ensure seamless hydration and performance. It also addresses common debugging scenarios, version-specific changes like the deprecation of data-wp-ignore, and the use of unique directive IDs for plugin compatibility.

What it does

  • Guides the implementation of interactive blocks using viewScriptModule.
  • Provides patterns for server-side rendering and state initialization in PHP.
  • Offers a debugging checklist for hydration issues and inert directives.
  • Explains how to use unique directive IDs to prevent attribute collisions.
  • Details the transition from deprecated directives to modern alternatives.
  • Assists in aligning build tooling with WordPress script conventions.

When to use it

  • When developing or debugging features using @wordpress/interactivity.
  • When troubleshooting hydration mismatches or layout shifts.
  • When implementing server-side state for SEO and performance.
  • When resolving conflicts between multiple plugins on the same DOM element.
  • When migrating legacy code away from deprecated directives like data-wp-ignore.

Advantages

  • Ensures consistent state between server-rendered HTML and client-side hydration.
  • Reduces layout shifts through proper server-side directive processing.
  • Provides clear patterns for managing complex state and derived data.
  • Facilitates easier debugging of interactive regions and event handlers.
  • Promotes best practices for modern WordPress block development.

Usage examples

“My interactive block is not responding to clicks. How do I debug this?”
The agent will guide you to verify the interactive root, check for view script module loading, confirm store namespace alignment, and inspect the console for errors.
“How do I initialize state in PHP for my interactive component?”
The agent will provide code examples using wp_interactivity_state() and wp_interactivity_data_wp_context() to ensure initial state is present in the server-rendered HTML.
“I have two plugins trying to attach a click handler to the same button. How do I fix this?”
The agent will explain how to use the --- separator to create unique directive IDs, allowing both handlers to coexist on the same element.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/wordpress/wp-interactivity-api/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip wp-interactivity-api.zip -d .agents/skills

Documentation by WordPress

WP Interactivity API

When to use

Use this skill when the user mentions:

  • Interactivity API, @wordpress/interactivity,

  • data-wp-interactive, data-wp-on--*, data-wp-bind--*, data-wp-context,

  • block viewScriptModule / module-based view scripts,

  • hydration issues or “directives don’t fire”.

Inputs required

  • Repo root + triage output (wp-project-triage).

  • Which block/theme/plugin surfaces are affected (frontend, editor, both).

  • Any constraints: WP version, whether modules are supported in the build.

Procedure

1) Detect existing usage + integration style

Search for:

  • data-wp-interactive

  • @wordpress/interactivity

  • viewScriptModule

Decide:

  • Is this a block providing interactivity via block.json view script module?

  • Is this theme-level interactivity?

  • Is this plugin-side “enhance existing markup” usage?

If you’re creating a new interactive block (not just debugging), prefer the official scaffold template:

  • @wordpress/create-block-interactive-template (via @wordpress/create-block)

2) Identify the store(s)

Locate store definitions and confirm:

  • state shape,

  • actions (mutations),

  • callbacks/event handlers used by data-wp-on--*.

3) Server-side rendering (best practice)

Pre-render HTML on the server before outputting to ensure:

  • Correct initial state in the HTML before JavaScript loads (no layout shift).

  • SEO benefits and faster perceived load time.

  • Seamless hydration when the client-side JavaScript takes over.

Enable server directive processing

For components using block.json, add supports.interactivity:

{
  "supports": {
    "interactivity": true
  }
}

For themes/plugins without block.json, use wp_interactivity_process_directives() to process directives.

Initialize state/context in PHP

Use wp_interactivity_state() to define initial global state:

wp_interactivity_state( 'myPlugin', array(
  'items'    => array( 'Apple', 'Banana', 'Cherry' ),
  'hasItems' => true,
));

For local context, use wp_interactivity_data_wp_context():

<?php
$context = array( 'isOpen' => false );
?>
<div <?php echo wp_interactivity_data_wp_context( $context ); ?>>
  ...
</div>
Define derived state in PHP

When derived state affects initial HTML rendering, replicate the logic in PHP:

wp_interactivity_state( 'myPlugin', array(
  'items'    => array( 'Apple', 'Banana' ),
  'hasItems' => function() {
    $state = wp_interactivity_state();
    return count( $state['items'] ) > 0;
  }
));

This ensures directives like data-wp-bind--hidden="!state.hasItems" render correctly on first load.

For detailed examples and patterns, see references/server-side-rendering.md.

4) Implement or change directives safely

When touching markup directives:

  • keep directive usage minimal and scoped,

  • prefer stable data attributes that map clearly to store state,

  • ensure server-rendered markup + client hydration align.

WordPress 6.9 changes:

  • data-wp-ignore is deprecated and will be removed in future versions. It broke context inheritance and caused issues with client-side navigation. Avoid using it.

  • Unique directive IDs: Multiple directives of the same type can now exist on one element using the --- separator (e.g., data-wp-on--click---plugin-a="..." and data-wp-on--click---plugin-b="...").

  • New TypeScript types: AsyncAction<ReturnType> and TypeYield<T> help with async action typing.

For quick directive reminders, see references/directives-quickref.md.

5) Build/tooling alignment

Verify the repo supports the required module build path:

  • if it uses @wordpress/scripts, prefer its conventions.

  • if it uses custom bundling, confirm module output is supported.

6) Debug common failure modes

If “nothing happens” on interaction:

  • confirm the viewScriptModule is enqueued/loaded,

  • confirm the DOM element has data-wp-interactive,

  • confirm the store namespace matches the directive’s value,

  • confirm there are no JS errors before hydration.

See references/debugging.md.

Verification

  • wp-project-triage indicates signals.usesInteractivityApi: true after your change (if applicable).

  • Manual smoke test: directive triggers and state updates as expected.

  • If tests exist: add/extend Playwright E2E around the interaction path.

Failure modes / debugging

  • Directives present but inert:

    • view script not loading, wrong module entrypoint, or missing data-wp-interactive.

  • Hydration mismatch / flicker:

    • server markup differs from client expectations; simplify or align initial state.

    • derived state not defined in PHP: use wp_interactivity_state() with closures.

  • Initial content missing or wrong:

    • supports.interactivity not set in block.json (for blocks).

    • wp_interactivity_process_directives() not called (for themes/plugins).

    • state/context not initialized in PHP before render.

  • Layout shift on load:

    • derived state like state.hasItems missing on server, causing hidden attribute to be absent.

  • Performance regressions:

    • overly broad interactive roots; scope interactivity to smaller subtrees.

  • Client-side navigation issues (WordPress 6.9):

    • getServerState() and getServerContext() now reset between page transitions—ensure your code doesn't assume stale values persist.

    • Router regions now support attachTo for rendering overlays (modals, pop-ups) dynamically.

Escalation

  • If repo build constraints are unclear, ask: "Is this using @wordpress/scripts or a custom bundler (webpack/vite)?"

  • Consult:

    • references/server-side-rendering.md

    • references/directives-quickref.md

    • references/debugging.md

Files

references/server-side-rendering.md

Server-Side Rendering for Interactivity API

  • Faster initial render: HTML arrives ready with correct values.

  • No layout shift: Hidden elements stay hidden from the first paint.

  • SEO benefits: Search engines see fully rendered content.

  • Graceful degradation: Content displays correctly even before JavaScript loads.

Setup Requirements

1. Enable Server Directive Processing

For components using block.json:

{
  "supports": {
    "interactivity": true
  }
}

For themes/plugins without block.json:

Use wp_interactivity_process_directives() to manually process directives (see "Themes and Plugins without block.json" section below).

2. Initialize Global State with wp_interactivity_state()

Define initial state values in PHP before rendering:

wp_interactivity_state( 'myPlugin', array(
  'fruits'    => array( 'Apple', 'Banana', 'Cherry' ),
  'isLoading' => false,
  'count'     => 3,
));

The state is serialized and available to client JavaScript automatically.

3. Initialize Local Context with wp_interactivity_data_wp_context()

For element-scoped context:

<?php
$context = array(
  'isOpen'   => false,
  'itemId'   => 42,
  'itemName' => 'Example',
);
?>
<div
  data-wp-interactive="myPlugin"
  <?php echo wp_interactivity_data_wp_context( $context ); ?>
>
  <button data-wp-on-async--click="actions.toggle">
    Toggle
  </button>
  <div data-wp-bind--hidden="!context.isOpen">
    Content for <?php echo esc_html( $context['itemName'] ); ?>
  </div>
</div>

Derived State on the Server

When derived state affects the initial HTML, define it in PHP to avoid layout shifts.

Static Derived State

When the derived value is known at render time:

$fruits    = array( 'Apple', 'Banana', 'Cherry' );
$hasFruits = count( $fruits ) > 0;

wp_interactivity_state( 'myPlugin', array(
  'fruits'    => $fruits,
  'hasFruits' => $hasFruits,
));
Dynamic Derived State (using closures)

When the value depends on context (e.g., inside data-wp-each loops):

wp_interactivity_state( 'myPlugin', array(
  'fruits'       => array( 'apple', 'banana', 'cherry' ),
  'shoppingList' => array( 'apple', 'cherry' ),
  'onShoppingList' => function() {
    $state   = wp_interactivity_state();
    $context = wp_interactivity_get_context();
    return in_array( $context['item'], $state['shoppingList'] ) ? 'Yes' : 'No';
  },
));

The closure is evaluated during directive processing for each element.

Complete Example: List with Server Rendering

PHP (render callback or template)
<?php
$fruits = array( 'Apple', 'Banana', 'Cherry' );

wp_interactivity_state( 'myFruitPlugin', array(
  'fruits'    => $fruits,
  'hasFruits' => count( $fruits ) > 0,
  'mango'     => __( 'Mango' ),
));
?>

<div data-wp-interactive="myFruitPlugin">
  <button data-wp-on-async--click="actions.addMango">
    <?php esc_html_e( 'Add Mango' ); ?>
  </button>
  <button data-wp-on-async--click="actions.clearAll">
    <?php esc_html_e( 'Clear All' ); ?>
  </button>

  <ul data-wp-bind--hidden="!state.hasFruits">
    <template data-wp-each="state.fruits">
      <li data-wp-text="context.item"></li>
    </template>
  </ul>

  <p data-wp-bind--hidden="state.hasFruits">
    <?php esc_html_e( 'No fruits available.' ); ?>
  </p>
</div>
JavaScript (view.js)
import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'myFruitPlugin', {
  state: {
    get hasFruits() {
      return state.fruits.length > 0;
    },
  },
  actions: {
    addMango() {
      state.fruits.push( state.mango );
    },
    clearAll() {
      state.fruits = [];
    },
  },
});
Rendered Output (initial HTML)
<div data-wp-interactive="myFruitPlugin">
  <button data-wp-on-async--click="actions.addMango">Add Mango</button>
  <button data-wp-on-async--click="actions.clearAll">Clear All</button>

  <ul>
    <li>Apple</li>
    <li>Banana</li>
    <li>Cherry</li>
  </ul>

  <p hidden>No fruits available.</p>
</div>

The hidden attribute is added server-side because state.hasFruits is true.

Serializing Values for Client Use

Use wp_interactivity_state() to pass server values to client JavaScript:

Translations
wp_interactivity_state( 'myPlugin', array(
  'labels' => array(
    'add'    => __( 'Add Item', 'textdomain' ),
    'remove' => __( 'Remove Item', 'textdomain' ),
    'empty'  => __( 'No items found', 'textdomain' ),
  ),
));
Ajax URLs and Nonces
wp_interactivity_state( 'myPlugin', array(
  'ajaxUrl' => admin_url( 'admin-ajax.php' ),
  'nonce'   => wp_create_nonce( 'myPlugin_nonce' ),
  'restUrl' => rest_url( 'myPlugin/v1/' ),
));
Client Usage
const { state } = store( 'myPlugin', {
  actions: {
    *fetchData() {
      const formData = new FormData();
      formData.append( 'action', 'my_action' );
      formData.append( '_ajax_nonce', state.nonce );

      const response = yield fetch( state.ajaxUrl, {
        method: 'POST',
        body: formData,
      });
      return yield response.json();
    },
  },
});

Themes and Plugins without block.json

For themes or plugins not using block.json, use wp_interactivity_process_directives():

<?php
wp_interactivity_state( 'myTheme', array(
  'menuOpen' => false,
));

ob_start();
?>

<nav
  data-wp-interactive="myTheme"
  data-wp-class--is-open="state.menuOpen"
>
  <button data-wp-on-async--click="actions.toggleMenu">
    Menu
  </button>
  <ul data-wp-bind--hidden="!state.menuOpen">
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

<?php
$html = ob_get_clean();
echo wp_interactivity_process_directives( $html );

Only call wp_interactivity_process_directives() once at the outermost template level.

PHP Helper Functions Reference

FunctionPurpose
wp_interactivity_state( $namespace, $state )Initialize/get global state for a namespace
wp_interactivity_data_wp_context( $context )Generate data-wp-context attribute
wp_interactivity_get_context( $namespace )Get current context during directive processing
wp_interactivity_process_directives( $html )Manually process directives (themes/plugins)

Common Pitfalls

Server Directive Processing Not Enabled

For block.json users: Without supports.interactivity, directives are not processed:

{
  "supports": {
    "interactivity": true
  }
}

For themes/plugins: Ensure wp_interactivity_process_directives() is called on the HTML output.

Derived State Missing on Server

If state.hasFruits is only defined in JavaScript, the hidden attribute won't be set:

<!-- Without server state: shows briefly then hides (layout shift) -->
<p data-wp-bind--hidden="state.hasFruits">No fruits</p>
State Not Matching Client Expectations

Ensure PHP and JavaScript derived state logic matches:

// PHP
'hasFruits' => count( $fruits ) > 0,
// JavaScript - must match PHP logic
get hasFruits() {
  return state.fruits.length > 0;
}

External References

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

The author says: Targets WordPress 7.0+ (PHP 7.4.0+). Filesystem-based agent with bash + node. Some workflows require WP-CLI.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
WordPress
Publisher
WordPress (Official)
Source
WordPress/agent-skills · skills/wp-interactivity-api · commit 7374e6f
Licence
GNU General Public License v2.0 or later (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by WordPress.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download wp-interactivity-api.zip