wp-blocks

WP Blocks, Block Themes, and Interactivity API

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "wp-blocks" with this command: npx skills add peixotorms/odinlayer-skills/peixotorms-odinlayer-skills-wp-blocks

WP Blocks, Block Themes, and Interactivity API

Consolidated skill for WordPress Gutenberg block development, block theme creation, and the Interactivity API. Targets WordPress 6.9+ (PHP 7.2.24+).

Part 1: Block Development

1.1 block.json Metadata

Every block starts with a block.json file. WordPress 6.9 enforces apiVersion 3.

{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "my-plugin/my-block", "version": "1.0.0", "title": "My Block", "category": "widgets", "icon": "smiley", "description": "A custom block.", "supports": { "html": false, "color": { "background": true, "text": true }, "typography": { "fontSize": true }, "spacing": { "margin": true, "padding": true } }, "attributes": { "content": { "type": "string", "source": "html", "selector": "p" }, "alignment": { "type": "string", "default": "none" } }, "textdomain": "my-plugin", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", "style": "file:./style-index.css", "render": "file:./render.php", "viewScriptModule": "file:./view.js" }

Required fields: apiVersion , name , title .

Asset fields:

Field Loads in Purpose

editorScript

Editor only Block registration and edit UI

editorStyle

Editor only Editor-specific styles

script

Both Shared JS (editor + frontend)

style

Both Shared styles

viewScript

Frontend only Classic frontend script

viewScriptModule

Frontend only Module-based frontend script (ES)

viewStyle

Frontend only Frontend-only styles

render

Server PHP render file for dynamic blocks

apiVersion 3 migration: Set "apiVersion": 3 , declare all style handles in block.json (missing handles will not load in iframed editor), test third-party scripts (window scoping differs). WordPress 7.0 will always use the iframe editor regardless of apiVersion.

1.2 Scaffolding

npx @wordpress/create-block my-block # Standard block npx @wordpress/create-block my-block --variant dynamic # Dynamic block with render.php npx @wordpress/create-block my-block --template @wordpress/create-block-interactive-template # Interactive

For manual setup: create block.json , register via register_block_type_from_metadata() in PHP, add editor JS and view assets.

1.3 Static vs Dynamic Rendering

Type When to use save() returns

Static Self-contained HTML, no server state dependency Full markup

Dynamic Server data (posts, user info, APIs), must stay current null (or minimal fallback)

  • Static: markup stored in DB; changing save() without deprecation causes "Invalid block" errors.

  • Dynamic: render via render.php or render_callback .

1.4 Wrapper Functions (Required)

Context Function

Editor (edit.js ) useBlockProps()

Static save (save.js ) useBlockProps.save()

Dynamic render (PHP) get_block_wrapper_attributes()

These inject classes, styles, and data attributes generated by block supports. Always spread on the outermost wrapper element.

1.5 Attributes and Serialization

Attributes persist via comment delimiter JSON (default), HTML source (source

  • selector ), or context (parent blocks).

Source Description

(none) Stored in block comment delimiter

attribute

Parsed from an HTML attribute (selector

  • attribute )

text

Parsed from element text content

html

Parsed from element inner HTML

query

Extracts an array from repeated elements

Rules: Avoid deprecated meta source. Avoid brittle selectors. Never change saved HTML without a deprecated entry.

1.6 InnerBlocks

For container blocks that nest other blocks. Use useInnerBlocksProps() in edit, useInnerBlocksProps.save() in save. Only one InnerBlocks per block. Use templateLock intentionally (false | 'all' | 'insert' | 'contentOnly' ).

1.7 Deprecations

Critical: When you change save() output or attribute shapes, add a deprecation entry. Order newest first. Each entry needs save matching old output. migrate is optional for attribute transforms. Never change save() without a deprecation entry.

1.8 Registration (PHP)

add_action( 'init', function() { register_block_type_from_metadata( DIR . '/build/blocks/my-block' ); } );

For dynamic blocks, pass 'render_callback' as second arg.

Detailed code examples (edit/save patterns, InnerBlocks, block supports, variations, styles, deprecation migrations, PHP registration): see resources/block-development.md

Part 2: Block Themes

2.1 Theme Structure

my-theme/ style.css # Theme header (required) theme.json # Global settings and styles templates/ index.html # Minimum required template single.html page.html archive.html 404.html parts/ header.html footer.html patterns/ hero.php styles/ dark.json # Style variation

2.2 theme.json Structure

theme.json (version 3) defines global settings and styles. Key sections:

Section Purpose

settings.color.palette

Custom color presets

settings.typography.*

Font families, sizes, line height

settings.spacing.*

Units, spacing size presets

settings.layout

contentSize and wideSize

settings.border

Border controls and radius presets

styles.color

Global background/text colors

styles.typography

Global font settings

styles.elements.*

Element styles (link, heading, button, input)

styles.blocks.*

Per-block style overrides

customTemplates

Custom template definitions

templateParts

Template part declarations (header/footer/etc)

Reference presets with var(--wp--preset--<type>--<slug>) .

Style hierarchy: core defaults -> theme.json -> child theme -> user customizations. User customizations stored in DB can override theme.json edits.

2.3 Templates and Template Parts

  • Templates in templates/ use block markup in HTML files.

  • Template parts in parts/ (flat, no subdirectories).

  • Reference parts via <!-- wp:template-part {"slug":"header","area":"header"} /--> .

2.4 Patterns

Filesystem patterns in patterns/*.php are auto-registered. Use docblock headers: Title , Slug , Categories , Keywords , Block Types , Post Types , Viewport Width . Add Inserter: no to hide from inserter.

2.5 Style Variations

JSON files under styles/ override settings and styles. Once a user selects a variation, the choice is stored in the database.

2.6 WordPress 6.9 theme.json Additions

  • Form element styling: styles.elements.input and styles.elements.select (border, color, outline, shadow, spacing). Focus state not yet supported.

  • Border radius presets: settings.border.radiusSizes for visual selection.

  • Button pseudo-classes: :hover and :focus states for Button block directly in theme.json.

Detailed code examples (full theme.json, templates, template parts, patterns, style variations, template hierarchy): see resources/block-themes.md

Part 3: Interactivity API

3.1 Directives Reference

Directive Purpose

data-wp-interactive

Declares an interactive region and namespace

data-wp-context

Provides per-element context (JSON)

data-wp-on--{event}

Attaches synchronous event handler

data-wp-on-async--{event}

Attaches async event handler (preferred)

data-wp-bind--{attr}

Binds a DOM attribute to state/context

data-wp-text

Sets element text content from state/context

data-wp-class--{name}

Toggles a CSS class based on state/context

data-wp-style--{prop}

Sets an inline style property

data-wp-each

Iterates over an array

data-wp-key

Unique key for list items

data-wp-watch

Runs a callback when dependencies change

data-wp-init

Runs once when the element is first connected

data-wp-run

Runs a callback on every render

3.2 Store Pattern

Define stores with store( namespace, { state, actions, callbacks } ) . Use getContext() for per-element context, getElement() for the current DOM ref. Async actions use generator syntax (*fetchData() { yield ... } ).

State vs context:

  • State is global, shared across all instances. Define with store() .

  • Context is per-element, scoped to nearest data-wp-context ancestor.

3.3 Server-Side Rendering (Required)

  • Set "supports": { "interactivity": true } in block.json.

  • Initialize state in PHP with wp_interactivity_state( $ns, $state ) .

  • Output context with wp_interactivity_data_wp_context( $context ) .

  • For themes/plugins without block.json, wrap HTML in wp_interactivity_process_directives() .

PHP helper functions:

Function Purpose

wp_interactivity_state( $ns, $state )

Initialize or get global state for a namespace

wp_interactivity_data_wp_context( $context )

Generate data-wp-context attribute string

wp_interactivity_get_context( $ns )

Get current context during directive processing

wp_interactivity_process_directives( $html )

Manually process directives (themes/plugins)

wp_interactivity_config( $ns, $config )

Set configuration data for a namespace

3.4 Hydration Rules

  • Client JS must produce markup matching server-rendered HTML.

  • Derived state in JS only (not PHP) causes layout shift (hidden not set server-side).

  • Ensure PHP and JS derived state logic matches exactly.

3.5 WordPress 6.9 Changes

  • data-wp-ignore is deprecated. Use conditional rendering or separate interactive regions.

  • Unique directive IDs: Use --- separator for multiple same-type directives on one element.

  • getServerState() / getServerContext() reset between client-side page transitions.

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

  • New TypeScript types: AsyncAction<ReturnType> and TypeYield<T> .

Detailed code examples (store definitions, SSR render.php, derived state closures, non-block usage, 6.9 directive IDs): see resources/interactivity-api.md

Part 4: Tooling

4.1 @wordpress/scripts

npx wp-scripts start # Development build with watch npx wp-scripts build # Production build npx wp-scripts lint-js # Lint JS npx wp-scripts lint-style # Lint CSS npx wp-scripts test-unit-js # Unit tests npx wp-scripts test-e2e # E2E tests

4.2 wp-env

npx wp-env start # Start environment npx wp-env stop # Stop environment npx wp-env run cli wp plugin list # Run WP-CLI commands npx wp-scripts test-e2e # Run E2E tests against the environment

4.3 Debugging Common Issues

"This block contains unexpected or invalid content":

  • You changed save() output or attribute parsing without a deprecation entry.

  • Fix: add a deprecated entry with the old save function and optionally a migrate function.

Block does not appear in inserter:

  • Confirm block.json name is valid and the block is registered.

  • Confirm build output exists and scripts are enqueued.

  • If using PHP registration, confirm register_block_type_from_metadata() runs on the init hook.

Attributes not saving:

  • Confirm attribute definition matches actual markup structure.

  • If the value is in comment delimiter JSON, avoid brittle HTML selectors.

  • Avoid the deprecated meta attribute source.

Styles not applying in editor (apiVersion 3):

  • Ensure style handles are declared in block.json (editorStyle , style ).

  • Styles not declared in block.json will not load inside the iframed editor.

Console warnings about apiVersion (WordPress 6.9+):

  • Update apiVersion to 3 in block.json. The warning only appears when SCRIPT_DEBUG is true.

Interactivity directives not firing:

  • Confirm the viewScriptModule is enqueued and loaded (check network tab).

  • Confirm the DOM element has data-wp-interactive with the correct namespace.

  • Confirm the store namespace matches the directive values.

  • Check console for JS errors before hydration.

  • Confirm supports.interactivity is set in block.json (or wp_interactivity_process_directives() is called).

Hydration mismatch / flicker:

  • Server markup differs from client expectations.

  • Derived state not defined in PHP causes missing attributes on initial render.

  • Ensure PHP and JS derived state logic matches.

Part 5: Common Mistakes

Mistake Consequence Fix

Changing save() without deprecation "Invalid block" error on existing posts Add deprecated array entry with old save

Renaming block name in block.json All existing instances break Treat name as immutable stable API

Missing useBlockProps() in edit Block supports (colors, spacing) not applied Always spread useBlockProps() on wrapper

Missing useBlockProps.save() in save Support classes/styles missing from saved markup Always spread on outermost save element

Missing get_block_wrapper_attributes() in PHP render Support classes/styles missing from frontend Always use on wrapper in render.php

Using innerHTML = for block save XSS risk and bypasses sanitization Use proper React components and RichText.Content

Attribute source selector too brittle Attribute value not found after minor markup change Use stable selectors or prefer comment delimiter

apiVersion below 3 Console warnings in 6.9; broken in 7.0 iframe editor Set apiVersion: 3 and test in iframe

Derived state only in JS, not PHP Layout shift on initial load; hidden not set server-side Define matching derived state in PHP with wp_interactivity_state()

Not declaring styles in block.json Styles load on frontend but not in iframed editor Add all handles to editorStyle / style fields

Using data-wp-ignore

Deprecated in 6.9; breaks context and navigation Use conditional rendering or separate regions

Template parts in subdirectories Parts not found by WordPress Keep parts flat in parts/ directory

User customizations overriding theme.json Theme changes appear ignored Check for DB-stored user overrides; reset if needed

Duplicate InnerBlocks in one block Runtime error Only one InnerBlocks per block

templateLock: 'all' without good reason Users cannot modify block content Use sparingly; prefer false or 'insert'

Resources

Detailed code examples and extended references are available in:

  • resources/block-development.md -- edit/save patterns, dynamic rendering, InnerBlocks composition, block supports (full JSON), block variations, block styles, deprecation migrations, PHP registration

  • resources/block-themes.md -- full theme.json example, template markup, template parts, pattern docblocks, style variations, template hierarchy

  • resources/interactivity-api.md -- store definitions, SSR render.php examples, derived state closures, non-block usage with wp_interactivity_process_directives() , PHP helper functions, WordPress 6.9 directive changes

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

elementor-hooks

No summary provided by upstream source.

Repository SourceNeeds Review
General

elementor-themes

No summary provided by upstream source.

Repository SourceNeeds Review
General

elementor-controls

No summary provided by upstream source.

Repository SourceNeeds Review
General

elementor-forms

No summary provided by upstream source.

Repository SourceNeeds Review