What if I told you that you could bind custom data from any source to WordPress Core blocks? I’d like to think that it would excite you and get those mental juices flowing.
I know when I first heard that this was possible, I couldn’t wait to try it out. I’ve had so many cool ideas over the years since the introduction of the Block Editor, but no good way to implement them within the system.
WordPress 6.5 will introduce Block Bindings, a new API that will open up a world of possibilities for anyone who needs to dynamically output data without writing custom blocks. In this post, you will learn how to register your own binding source and attach whatever data you want to Core blocks (at least within the limits of what’s possible in 6.5).
In the past few weeks, I’ve built over a dozen features with this new API, and it’s only fair that I share how to do this with you.
As you learned in the previous post in this series, the Block Bindings API serves as the mechanism for binding attributes to any type of source. WordPress 6.5 will ship two built-in binding sources:
core/post-meta: The source for binding custom fields.
core/pattern-overrides: The source for handling pattern overrides, another WordPress 6.5 feature.Punted to WordPress 6.6.
For extenders, the core/post-meta source is obviously needed for a lot of projects. But there are many scenarios where you might need to bind data from a different source altogether. Some ideas that come to mind are:
Taxonomy term and user data
WordPress site data
Plugin/theme options
Custom database tables
A third-party API
There are plans to ship core/site-data, core/user-data, and other binding sources for handling Core data in future releases, but you can certainly start building them on your own in the meantime.
At the block level, custom binding sources work in the exact same way as the core/post-meta source. The big difference is that you have full control over how bindings work under the hood when you register a custom one. And you also have to use the Block Bindings API to register your source.
As discussed in the first post in this series, bindings are limited to the Image, Paragraph, Heading, and Button blocks in WordPress 6.5. Wider support is expected in future versions.
Getting to know the API functions
WordPress 6.5 provides a new register_block_bindings_source() function for registering custom binding sources. It is also used internally to register the custom field and future pattern override sources.
$source_name: A unique name for your custom binding source in the form of namespace/slug.
$source_properties: An array of properties to define your binding source:
label: An internationalized text string to represent the binding source. Note: this is not currently shown anywhere in the UI.
get_value_callback: A PHP callable (function, closure, etc.) that is called when a block’s attribute matches the $source_name parameter.
uses_context:(Optional) Extends the block instance with an array of contexts if needed for the callback. For example, if you need the current post ID, you’d set this to [ 'postId' ].
When registering a custom source, you do so on the init hook. So let’s look at an example of what this might look like (we’ll get to a real example in the next section):
That is all the code that is required for registering your binding source with WordPress.
When WordPress comes across your projectslug/example-source binding source while parsing a block, it will run your callback function. Your function signature should look like this:
It can accept up to three parameters, but you don’t need to define each if you do not need them:
$source_args: An array of arguments passed via the metadata.bindings.$attribute.args property from the block.
$block_instance: The current instance of the block the binding is connected to as a WP_Block object.
$attribute_name: The current attribute set via the metadata.bindings.$attribute property on the block.
To put all of this in the proper context, let’s jump into some real examples.
Decisions: defining the structure of custom bindings
You’ve already learned the basics of how bindings work in Part 1 of this series. Because custom bindings are pretty similar, let’s kick this up another notch and build something slightly more advanced. But don’t worry too much about the complexity—we’re still just covering the basics of what’s possible with the Block Bindings API.
Suppose that you wanted to build a block that showcased a user card. Maybe this is for a company’s team profiles or something along those lines. Depending on the complexity of your user card needs, you might not need a custom block at all. It’s possible you could build it with Core blocks by binding user data to them.
Here’s a screen grab of what we’ll build in the upcoming sections:
In the screenshot, there are three blocks with dynamic data:
User display name: Bound to the content of a Heading block
Avatar: Bound to the URL of an Image block
Bio/Description: Bound to the content of a Paragraph block
Knowing what type of data you need upfront is a crucial part of deciding how your custom binding source will work. Because this is user data, you know that you’ll need the user ID. You also need to access those user data fields individually, so you’ll need an argument for those.
That means the structure of your binding source should expect two arguments:
userId: To determine which user’s data to get
key: To bind individual user data fields
Those argument names can be anything you want them to be. I decided to use them because they made the most sense to me, but you should use what’s best for your project.
Here’s what the bindings structure will look like when we use it in the editor later:
Please don’t skip this step of deciding how you will structure your accepted arguments. This is to avoid management headaches down the road.
Registering a custom binding source
With the expected arguments figured out, it’s time to actually write the code for handling your custom binding source.
You’ll use the register_block_bindings_source() function to register a new projectslug/user-data source. Add this code to a custom plugin file or your theme’s functions.php: