Custom block bindings for dynamic content in WordPress 6.9

A client of mine recently, someone who sweats the details, needed something awkward on their product listings. They wanted custom product data shown right on the single product pages. Nothing exotic, just a few attributes that were not standard WooCommerce fields. The catch was that they wanted their marketing team to have some room to move, a way to choose which custom attribute appears, right there in the editor. Standard Block Bindings in WordPress got us part of the way, but they did not expose the specific options we needed, so we went to Custom Block Bindings.

This is a common headache. Your data lives in custom fields, maybe an external API, maybe some odd internal logic, and the default editor just stares back at you. You can hardcode it, sure, but then every small change needs a developer, which is miserable for clients who want to manage their own content.

My first thought was to build a dedicated custom block for each piece of dynamic data, or pull ACF fields in by hand with a bit of PHP. That works. It always does. But it feels heavy and clumsy, like bringing a tank to a knife fight. There had to be a cleaner way to work with the existing Block Bindings UI so it felt native rather than bolted on.

Dynamic content with custom block bindings

This is where WordPress 6.9 helps, with its Block Bindings improvements. For developers, the useful part is registering custom sources in the editor UI through a getFieldsList method. It lets you surface your own data sources in a dropdown, the same way core Block Bindings expose post meta or site data. The editor gets the control, and content creators do not have to learn shortcodes or custom functions to use it. It follows the approach laid out in the dev note, which makes dynamic content genuinely editable.

Here is a simplified example of exposing your own custom fields to the Block Bindings UI. The snippet registers a custom source, call it ‘bbioon_custom_data’, which can fetch whatever you need:

<?php
/**
 * Plugin Name: Bbioon Block Bindings Custom Source
 * Description: Registers a custom Block Bindings source for dynamic content.
 * Version: 1.0.0
 * Author: Ahmad Wael
 */

function bbioon_register_custom_block_bindings_source() {
    // This part runs on the server to provide the actual values
    wp_register_script(
        'bbioon-custom-bindings-source',
        plugin_dir_url( __FILE__ ) . 'build/index.js',
        array( 'wp-blocks', 'wp-element', 'wp-data', 'wp-edit-post' ),
        '1.0.0',
        true
    );

    // Enqueue the script only when Block Editor is active
    add_action( 'enqueue_block_editor_assets', function() {
        wp_enqueue_script( 'bbioon-custom-bindings-source' );
    });

    // You can also use the new filter block_bindings_supported_attributes_{$block_type}
    // to limit which block attributes can be bound to your source.
    // add_filter( 'block_bindings_supported_attributes_core/paragraph', 'bbioon_limit_paragraph_bindings' );
    // function bbioon_limit_paragraph_bindings( $attributes ) {
    //     $attributes[] = 'bbioonCustomAttribute'; // Only allow binding to this custom attribute
    //     return $attributes;
    // }
}
add_action( 'init', 'bbioon_register_custom_block_bindings_source' );
?>

// JavaScript for the editor (build/index.js)
wp.blocks.registerBlockBindingsSource({
    name: 'bbioon/custom-data',
    label: 'Bbioon Custom Data',
    getValues: ({ bindings }) => {
        // Implement logic to fetch data based on selected field and args
        const field = bindings.content?.args?.bbioon_field;
        let value = '';

        if (field === 'special_product_note') {
            value = 'This product features eco-friendly materials.';
        } else if (field === 'delivery_estimate') {
            value = 'Ships in 3-5 business days.';
        }
        // In a real scenario, you'd fetch this from post meta or an API.
        return {
            content: value || bindings.content,
        };
    },
    getFieldsList() {
        return [
            {
                label: 'Special Product Note',
                type: 'string', // Must match attribute type
                args: {
                    bbioon_field: 'special_product_note',
                },
            },
            {
                label: 'Delivery Estimate',
                type: 'string',
                args: {
                    bbioon_field: 'delivery_estimate',
                },
            },
        ];
    },
});

See that getFieldsList() method? That is the important bit. It populates the dropdown in the editor, so a user can pick “Special Product Note” or “Delivery Estimate” for a paragraph block. The type property matters too, since it keeps incompatible fields out of the list. And do not forget the block_bindings_supported_attributes_{$block_type} filter. That is your server-side control over which attributes can be bound at all, which keeps a tight rein on your block themes.

Make the editor work with you

The takeaway is about integration and keeping developers sane. Rather than fighting the editor or piling on custom workarounds, WordPress 6.9 gives you the tools to extend what it already does. You expose dynamic content options that feel natural to content creators while keeping firm control over the data on the backend. Content creators get a better experience, and we get a cleaner codebase.

This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably run into it before.

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.