diff --git a/skills/wp-interactivity-api/SKILL.md b/skills/wp-interactivity-api/SKILL.md index 6f3072d..84a0736 100644 --- a/skills/wp-interactivity-api/SKILL.md +++ b/skills/wp-interactivity-api/SKILL.md @@ -13,7 +13,8 @@ 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”. +- hydration issues or "directives don't fire", +- **client-side navigation**, `@wordpress/interactivity-router`, `data-wp-router-region`. ## Inputs required @@ -134,16 +135,55 @@ 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 +### 6) Client-side navigation with the Router -If “nothing happens” on interaction: +**CRITICAL: The router automatically intercepts `` links. Do NOT add custom click handlers.** + +When using `@wordpress/interactivity-router` for SPA-like navigation: + +1. Enqueue the router: `wp_enqueue_script_module('my-nav', '...', ['@wordpress/interactivity', '@wordpress/interactivity-router'])` +2. **Mark module as compatible (WordPress 6.9+ REQUIRED):** + ```php + wp_interactivity()->add_client_navigation_support_to_script_module('my-nav'); + ``` +3. Add `data-wp-router-region="region-id"` to the content area to be swapped +4. Use **plain `` links** - NO `data-wp-on--click` handlers! + +The router automatically: +- Intercepts all same-origin link clicks +- Fetches the target page +- Swaps ONLY the matching router region content +- Updates browser URL via History API + +**WordPress 6.9+ CRITICAL:** Script modules must be marked as compatible with client-side navigation: +- For blocks: use `"supports": { "interactivity": { "clientNavigation": true } }` in block.json +- For manual modules: call `wp_interactivity()->add_client_navigation_support_to_script_module('module-id')` + +Without this, the router will NOT intercept link clicks. The script tag must have `data-wp-router-options` attribute. + +Reference: https://make.wordpress.org/core/2025/11/12/interactivity-apis-client-navigation-improvements-in-wordpress-6-9/ + +**Common mistake:** Adding `data-wp-on--click="actions.navigate"` to nav elements. This breaks the router. Remove it. + +See `references/router.md` for full details. + +### 7) 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 the store namespace matches the directive's value, - confirm there are no JS errors before hydration. -See `references/debugging.md`. +If client-side **navigation** isn't working: +- confirm `@wordpress/interactivity-router` is enqueued, +- confirm `data-wp-router-region` exists on both source and target pages with SAME ID, +- **confirm script module has `data-wp-router-options` attribute** (WP 6.9+) - if missing, call `wp_interactivity()->add_client_navigation_support_to_script_module('your-module')`, +- **remove any custom `data-wp-on--click` handlers from navigation links**, +- links should be plain `` tags. + +See `references/debugging.md` and `references/router.md`. ## Verification @@ -174,6 +214,7 @@ See `references/debugging.md`. - If repo build constraints are unclear, ask: "Is this using `@wordpress/scripts` or a custom bundler (webpack/vite)?" - Consult: + - `references/router.md` - **READ THIS FIRST for navigation issues** - `references/server-side-rendering.md` - `references/directives-quickref.md` - `references/debugging.md` diff --git a/skills/wp-interactivity-api/references/router.md b/skills/wp-interactivity-api/references/router.md new file mode 100644 index 0000000..cbff6eb --- /dev/null +++ b/skills/wp-interactivity-api/references/router.md @@ -0,0 +1,152 @@ +# Interactivity Router (`@wordpress/interactivity-router`) + +## Key Concept: Automatic Link Interception + +**The router automatically intercepts ALL same-origin `` link clicks.** You do NOT need custom click handlers. + +## How It Works + +When `@wordpress/interactivity-router` is enqueued and `data-wp-router-region` exists on the page: + +1. User clicks any `` link (same-origin) +2. Router intercepts the click automatically (calls `preventDefault`) +3. Fetches the target page via AJAX +4. Finds the matching `data-wp-router-region` on the new page +5. Swaps ONLY that region's innerHTML +6. Updates the browser URL via History API +7. Sidebar, header, footer stay intact + +## Required Setup + +```php +// 1. Enqueue the router module +wp_enqueue_script_module( + 'my-navigation', + get_stylesheet_directory_uri() . '/assets/js/navigation.js', + ['@wordpress/interactivity', '@wordpress/interactivity-router'], + '1.0.0' +); + +// 2. Mark module as compatible with client-side navigation (WordPress 6.9+) +// This is REQUIRED for manually registered script modules! +wp_interactivity()->add_client_navigation_support_to_script_module('my-navigation'); +``` + +```php +// 3. Add router region to the content area (via filter or directly in template) +
+ +
+``` + +```html + +
+``` + +## WordPress 6.9+ Requirements + +**CRITICAL: Script modules must be marked as compatible with client-side navigation.** + +For blocks, this is automatic when `block.json` has: +```json +{ + "supports": { + "interactivity": { "clientNavigation": true } + } +} +``` + +For manually registered script modules (themes/plugins), you MUST call: +```php +wp_interactivity()->add_client_navigation_support_to_script_module('my-module'); +``` + +This adds `data-wp-router-options='{"loadOnClientNavigation":true}'` to the script tag. + +**Without this, the router will not intercept link clicks.** + +Reference: https://make.wordpress.org/core/2025/11/12/interactivity-apis-client-navigation-improvements-in-wordpress-6-9/ + +## Common Mistakes + +### ❌ WRONG: Adding click handlers to links + +```html + + +``` + +```javascript +// DON'T DO THIS +store('my/nav', { + actions: { + navigate(e) { + e.preventDefault(); + routerActions.navigate(e.target.href); + } + } +}); +``` + +### ✅ CORRECT: Plain links, router does the work + +```html + + +``` + +```javascript +// Just register an empty store if needed, or don't register one at all +import { store } from '@wordpress/interactivity'; +store('my/namespace', {}); +``` + +## When You DO Need Custom Handlers + +Only use custom navigation handlers for: + +- Non-link elements (buttons, divs) that should navigate +- Conditional navigation (confirm dialogs, form validation) +- Links that need special processing before navigation + +```javascript +// Example: Button that navigates +store('my/namespace', { + actions: { + *navigateToProfile() { + const { actions } = yield import('@wordpress/interactivity-router'); + yield actions.navigate('/profile/'); + } + } +}); +``` + +## Debugging + +If navigation isn't working: + +1. **Check router is enqueued**: View page source, search for `interactivity-router` +2. **Check region exists**: Search HTML for `data-wp-router-region` +3. **Check target page has same region ID**: Both pages need matching region IDs +4. **Check for custom handlers**: Remove any `data-wp-on--click` from nav elements +5. **Check console errors**: Router logs navigation events +6. **Check script module has router options (WP 6.9+)**: Your script tag should have `data-wp-router-options` attribute. If missing, call `wp_interactivity()->add_client_navigation_support_to_script_module('your-module')` + +## Multiple Router Regions + +You can have multiple independent regions: + +```html + +
...
+``` + +Each region swaps independently based on the target page's matching regions.