Claude Skill

wp-block-bindings-api

Create or audit WordPress Block Bindings sources that connect block attributes to post meta, post/term data, pattern overrides, custom tables, or remote data. Covers PHP source registration, editor registration, metadata.bindings markup, supported-attribute filters, context, edit

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download lonsdale201-wp-agent-skills-wordpress_wp-block-bindings-api-52f6020.zip · 4 KB
Part of lonsdale201/wp-agent-skills — 226 skills

Install

skills CLI npx skills add https://github.com/Lonsdale201/wp-agent-skills/tree/main/wordpress/wp-block-bindings-api
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lonsdale201-wp-agent-skills@llmmart
Git git clone https://github.com/Lonsdale201/wp-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lonsdale201/wp-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

WordPress Block Bindings API

Block Bindings replace selected block attributes at render time from a registered data source. Use them when content should stay dynamic while remaining a normal block. They do not persist data by themselves and do not make an unsupported attribute bindable automatically.

Decide before implementing

Need Use
Bind a supported block attribute to dynamic data Block Bindings
Change the complete rendered structure Dynamic block render.php
Store and validate a custom field Metadata/REST registration plus a binding
Merely transform final HTML Render filter or HTML API

Register the server source

Register on init. The name must be lowercase namespace/name and the callback must be callable.

add_action( 'init', static function (): void {
    register_block_bindings_source(
        'acme/catalog-field',
        array(
            'label'              => __( 'Catalog field', 'acme' ),
            'uses_context'       => array( 'postId' ),
            'get_value_callback' => static function ( array $args, WP_Block $block, string $attribute ) {
                $allowed = array( 'sku', 'subtitle' );
                $key     = isset( $args['key'] ) ? sanitize_key( $args['key'] ) : '';

                if ( ! in_array( $key, $allowed, true ) ) {
                    return null;
                }

                $post_id = isset( $block->context['postId'] ) ? (int) $block->context['postId'] : 0;
                return $post_id ? get_post_meta( $post_id, '_acme_' . $key, true ) : null;
            },
        )
    );
} );

The callback receives binding args, the current WP_Block, and the target attribute name. The args originate in block content and are untrusted. Allowlist keys, validate IDs, and return null when no safe value is available.

Bind the attribute

<!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"acme/catalog-field","args":{"key":"subtitle"}}}}} -->
<p>Fallback subtitle</p>
<!-- /wp:paragraph -->

Do not delete useful fallback markup. If the source is missing, disabled, or returns no usable value, a stable fallback makes the content more portable.

Supported attributes in WordPress 7.1

Core's default list is:

Block Attributes
core/paragraph content
core/heading content
core/list-item content (added in 7.1)
core/image id, url, title, alt, caption
core/button url, text, linkTarget, rel
core/post-date datetime
core/navigation-link, core/navigation-submenu url

Query the truth with get_block_bindings_supported_attributes( $block_type ); do not freeze this table into plugin logic.

For a custom block or deliberately supported attribute, extend the list narrowly:

add_filter( 'block_bindings_supported_attributes_acme/card', static function ( array $attributes ): array {
    $attributes[] = 'title';
    return array_values( array_unique( $attributes ) );
} );

Only expose attributes whose render pipeline correctly consumes the bound value. A filter entry alone cannot repair custom rendering that ignores parsed attributes.

Editor registration is a separate concern

PHP registration makes frontend/server rendering work. Register the same source in the editor when users need previews, editing, or the binding picker. The editor contract can provide getValues, setValues, canUserEditValue, and, since 6.9, getFieldsList.

Use useBlockBindingsUtils() from @wordpress/block-editor to update or remove metadata.bindings; do not hand-mutate nested block attributes. Keep the source name and argument schema identical in PHP and JavaScript.

Read references/editor-and-security.md before implementing editable bindings.

Core sources

Prefer an existing Core source when it fits, notably core/post-meta, core/post-data, core/term-data, or core/pattern-overrides. For post meta, register the field correctly and expose it to REST/editor contexts as required. Protected meta is not made public merely because a block references it, but your custom callback can accidentally leak it if it skips authorization and context checks.

Performance rules

Binding callbacks can run once per bound attribute for every rendered block.

  • Never issue an uncached remote request per callback.
  • Cache repeated lookups within the request by source arguments and object ID.
  • Prime metadata/object caches for repeated objects where appropriate.
  • Keep callbacks deterministic for the same render context; randomness breaks page caches and editor/frontend parity.
  • Avoid writes, analytics events, or other side effects in get_value_callback.

Security checklist

  • Treat args and block markup as attacker-controlled input.
  • Check the current object/site context; do not trust a postId supplied in args.
  • Never return secrets or private metadata to public frontend output.
  • Enforce capabilities in editor write paths and REST endpoints, not only in UI predicates.
  • Sanitize for storage in the write API; escape at the final block output context.
  • Do not return raw HTML for a plain string attribute unless that block's rendering contract safely handles it.

Verification

  1. Assert source registration after init with get_block_bindings_source().
  2. Render bound markup through do_blocks() and check the exact output.
  3. Test unknown args, missing object context, deleted source, and null return.
  4. Test editor preview separately from frontend rendering.
  5. Measure query/request counts across a post with many bound blocks.
  6. On 7.1, verify core/list-item content bindings and backward compatibility for older supported Core versions.

Related skills

  • wordpress/wp-metadata-api for registered post, user, term, and comment meta.
  • wordpress/wp-rest-api for authorized editor write endpoints.
  • wordpress/wp-html-api for safe rendered-markup changes.
  • wordpress/wp-interactivity-api when the bound output also needs reactive frontend behavior.

References

Files (wp-agent-skills)
  • agents
    • openai.yaml 115 B
      interface:
        display_name: "WP Block Bindings API"
        short_description: "Connect block attributes to dynamic data"
      
  • references
    • editor-and-security.md 2.3 KB
      # Block Bindings editor and security reference
      
      ## Two registrations, two responsibilities
      
      | Layer | Responsibility |
      |---|---|
      | PHP `register_block_bindings_source()` | Authoritative server/frontend value resolution |
      | Editor `registerBlockBindingsSource()` | Editor preview, binding UI, and optional edit behavior |
      
      A JavaScript-only source cannot make PHP render dynamic frontend values. A PHP-only source can render correctly but may provide a poor editing experience.
      
      ## Editor callbacks
      
      - `getValues( { bindings, clientId, context, select } )` returns an object keyed by block attribute.
      - `setValues( { bindings, clientId, context, dispatch, select } )` persists edited values. Treat `newValue` as untrusted and use an authorized data/REST layer.
      - `canUserEditValue()` controls the editor experience, not server authorization.
      - `getFieldsList()` returns picker entries with a `label`, compatible `type`, and source `args`.
      - `usesContext` should match the server source. Do not redundantly redefine it in JavaScript when server registration already supplies it to the editor contract.
      
      Use `useBlockBindingsUtils( clientId )` for binding metadata:
      
      ```js
      import { useBlockBindingsUtils } from '@wordpress/block-editor';
      
      const { updateBlockBindings, removeAllBlockBindings } =
      	useBlockBindingsUtils( clientId );
      
      updateBlockBindings( {
      	content: {
      		source: 'acme/catalog-field',
      		args: { key: 'subtitle' },
      	},
      } );
      ```
      
      ## Threat model
      
      Post content can be imported, edited through REST, copied from another site, or authored by a user with fewer privileges than the data source owner. Consequently:
      
      - binding source names and args are not trusted configuration;
      - object IDs in args must not override the actual rendering context without authorization;
      - `canUserEditValue()` cannot replace a server capability check;
      - the frontend callback must not reveal data merely because the editor previously allowed the binding;
      - remote sources require URL allowlists, timeouts, caching, and SSRF-safe HTTP APIs.
      
      ## Portable fallback behavior
      
      Keep static block content meaningful. Test these states:
      
      1. plugin active and data exists;
      2. plugin active but data is absent;
      3. source rejects malformed args;
      4. plugin/source disabled;
      5. block copied to another site.
      
      The content should remain valid HTML in every state.
      
  • SKILL.md 7.1 KB
    ---
    name: wp-block-bindings-api
    description: "Create or audit WordPress Block Bindings sources that connect block attributes to post meta, post/term data, pattern overrides, custom tables, or remote data. Covers PHP source registration, editor registration, metadata.bindings markup, supported-attribute filters, context, editing callbacks, permissions, caching, and the WordPress 7.1 List Item addition."
    license: GPLv2-or-later
    metadata:
      wp-skills-author: "Soczó Kristóf"
      wp-skills-contact: "mailto:lonsdale201@hotmail.com"
      wp-skills-plugin: "wordpress"
      wp-skills-plugin-version-tested: "6.5 - 7.1"
      wp-skills-wp-version-tested: "7.1"
      wp-skills-php-min: "7.4"
      wp-skills-last-updated: "2026-08-20"
    ---
    
    # WordPress Block Bindings API
    
    Block Bindings replace selected block attributes at render time from a registered data source. Use them when content should stay dynamic while remaining a normal block. They do not persist data by themselves and do not make an unsupported attribute bindable automatically.
    
    ## Decide before implementing
    
    | Need | Use |
    |---|---|
    | Bind a supported block attribute to dynamic data | Block Bindings |
    | Change the complete rendered structure | Dynamic block `render.php` |
    | Store and validate a custom field | Metadata/REST registration plus a binding |
    | Merely transform final HTML | Render filter or HTML API |
    
    ## Register the server source
    
    Register on `init`. The name must be lowercase `namespace/name` and the callback must be callable.
    
    ```php
    add_action( 'init', static function (): void {
        register_block_bindings_source(
            'acme/catalog-field',
            array(
                'label'              => __( 'Catalog field', 'acme' ),
                'uses_context'       => array( 'postId' ),
                'get_value_callback' => static function ( array $args, WP_Block $block, string $attribute ) {
                    $allowed = array( 'sku', 'subtitle' );
                    $key     = isset( $args['key'] ) ? sanitize_key( $args['key'] ) : '';
    
                    if ( ! in_array( $key, $allowed, true ) ) {
                        return null;
                    }
    
                    $post_id = isset( $block->context['postId'] ) ? (int) $block->context['postId'] : 0;
                    return $post_id ? get_post_meta( $post_id, '_acme_' . $key, true ) : null;
                },
            )
        );
    } );
    ```
    
    The callback receives binding `args`, the current `WP_Block`, and the target attribute name. The args originate in block content and are untrusted. Allowlist keys, validate IDs, and return `null` when no safe value is available.
    
    ## Bind the attribute
    
    ```html
    <!-- wp:paragraph {"metadata":{"bindings":{"content":{"source":"acme/catalog-field","args":{"key":"subtitle"}}}}} -->
    <p>Fallback subtitle</p>
    <!-- /wp:paragraph -->
    ```
    
    Do not delete useful fallback markup. If the source is missing, disabled, or returns no usable value, a stable fallback makes the content more portable.
    
    ## Supported attributes in WordPress 7.1
    
    Core's default list is:
    
    | Block | Attributes |
    |---|---|
    | `core/paragraph` | `content` |
    | `core/heading` | `content` |
    | `core/list-item` | `content` (added in 7.1) |
    | `core/image` | `id`, `url`, `title`, `alt`, `caption` |
    | `core/button` | `url`, `text`, `linkTarget`, `rel` |
    | `core/post-date` | `datetime` |
    | `core/navigation-link`, `core/navigation-submenu` | `url` |
    
    Query the truth with `get_block_bindings_supported_attributes( $block_type )`; do not freeze this table into plugin logic.
    
    For a custom block or deliberately supported attribute, extend the list narrowly:
    
    ```php
    add_filter( 'block_bindings_supported_attributes_acme/card', static function ( array $attributes ): array {
        $attributes[] = 'title';
        return array_values( array_unique( $attributes ) );
    } );
    ```
    
    Only expose attributes whose render pipeline correctly consumes the bound value. A filter entry alone cannot repair custom rendering that ignores parsed attributes.
    
    ## Editor registration is a separate concern
    
    PHP registration makes frontend/server rendering work. Register the same source in the editor when users need previews, editing, or the binding picker. The editor contract can provide `getValues`, `setValues`, `canUserEditValue`, and, since 6.9, `getFieldsList`.
    
    Use `useBlockBindingsUtils()` from `@wordpress/block-editor` to update or remove `metadata.bindings`; do not hand-mutate nested block attributes. Keep the source name and argument schema identical in PHP and JavaScript.
    
    Read [references/editor-and-security.md](references/editor-and-security.md) before implementing editable bindings.
    
    ## Core sources
    
    Prefer an existing Core source when it fits, notably `core/post-meta`, `core/post-data`, `core/term-data`, or `core/pattern-overrides`. For post meta, register the field correctly and expose it to REST/editor contexts as required. Protected meta is not made public merely because a block references it, but your custom callback can accidentally leak it if it skips authorization and context checks.
    
    ## Performance rules
    
    Binding callbacks can run once per bound attribute for every rendered block.
    
    - Never issue an uncached remote request per callback.
    - Cache repeated lookups within the request by source arguments and object ID.
    - Prime metadata/object caches for repeated objects where appropriate.
    - Keep callbacks deterministic for the same render context; randomness breaks page caches and editor/frontend parity.
    - Avoid writes, analytics events, or other side effects in `get_value_callback`.
    
    ## Security checklist
    
    - Treat `args` and block markup as attacker-controlled input.
    - Check the current object/site context; do not trust a `postId` supplied in args.
    - Never return secrets or private metadata to public frontend output.
    - Enforce capabilities in editor write paths and REST endpoints, not only in UI predicates.
    - Sanitize for storage in the write API; escape at the final block output context.
    - Do not return raw HTML for a plain string attribute unless that block's rendering contract safely handles it.
    
    ## Verification
    
    1. Assert source registration after `init` with `get_block_bindings_source()`.
    2. Render bound markup through `do_blocks()` and check the exact output.
    3. Test unknown args, missing object context, deleted source, and `null` return.
    4. Test editor preview separately from frontend rendering.
    5. Measure query/request counts across a post with many bound blocks.
    6. On 7.1, verify `core/list-item` content bindings and backward compatibility for older supported Core versions.
    
    ## Related skills
    
    - `wordpress/wp-metadata-api` for registered post, user, term, and comment meta.
    - `wordpress/wp-rest-api` for authorized editor write endpoints.
    - `wordpress/wp-html-api` for safe rendered-markup changes.
    - `wordpress/wp-interactivity-api` when the bound output also needs reactive frontend behavior.
    
    ## References
    
    - Read `references/editor-and-security.md` for editor registration, editing callbacks, permission and caching detail.
    - Miscellaneous block editor changes in WordPress 7.1 (List Item binding): <https://make.wordpress.org/core/2026/08/04/miscellaneous-block-editor-changes-in-wordpress-7-1/>
    - WordPress 7.1 Field Guide: <https://make.wordpress.org/core/2026/08/05/wordpress-7-1-field-guide/>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related