The previous lesson’s reading-time source computed a value from data already on the
site. This lesson builds a source that calls an AI model to produce the value:
binding an image block’s alt attribute to an AI-generated description of that image.
It’s a genuinely useful pattern, and also a good place to be honest about a real
limitation of the Block Bindings API as it exists today, register_block_bindings_source()
has no update_value_callback, so this source is inherently read-oriented.
The previous lesson’s register_block_bindings_source() walkthrough. An AI provider
plugin and working API key (Course 5), since this source makes a real
wp_ai_client_prompt() call.
Step 1: Register the source
Same registration shape as before, on init, with a namespaced name and a label:
// File: my-plugin/my-plugin.php
add_action( 'init', function () {
register_block_bindings_source(
'my-plugin/ai-image-alt',
array(
'label' => __( 'AI Generated Alt Text', 'my-plugin' ),
'get_value_callback' => 'my_plugin_get_ai_alt_text',
)
);
} );
This source doesn’t need uses_context, everything it needs (which attachment the
image block points at) is already available on the block instance itself, not
something it has to read from an ancestor block.
Step 2: Reading the attachment ID from the block instance
The image block’s own attributes, id and url, are available on
$block_instance->attributes, the same object passed into get_value_callback. That
means this source can resolve the attachment ID directly, without needing any args
passed through the binding’s own markup:
// File: my-plugin/my-plugin.php
function my_plugin_get_ai_alt_text( $source_args, $block_instance, $attribute_name ) {
if ( 'alt' !== $attribute_name ) {
return null;
}
$attachment_id = $block_instance->attributes['id'] ?? 0;
if ( ! $attachment_id ) {
return null;
}
// ... generation and caching, Step 3.
}
Checking $attribute_name matters if you ever reuse one source function across more
than one bound attribute, it’s the only argument that tells you which attribute is
currently being resolved.
Step 3: Generating once, caching after
This is the step that matters most. get_value_callback runs every time the block
renders, which on a published post means every page view, not once. Calling
wp_ai_client_prompt() on every render would be slow, expensive, and pointless, an
image’s alt description doesn’t change between requests. Cache the result as
attachment meta and only call the model when nothing is cached yet:
// File: my-plugin/my-plugin.php
function my_plugin_get_ai_alt_text( $source_args, $block_instance, $attribute_name ) {
if ( 'alt' !== $attribute_name ) {
return null;
}
$attachment_id = $block_instance->attributes['id'] ?? 0;
if ( ! $attachment_id ) {
return null;
}
$cached = get_post_meta( $attachment_id, '_ai_alt_text', true );
if ( $cached ) {
return $cached;
}
$image_url = wp_get_attachment_image_url( $attachment_id, 'medium' );
if ( ! $image_url ) {
return null;
}
$result = wp_ai_client_prompt()
->with_file( $image_url )
->with_text( 'Write a concise, descriptive alt text for this image, under 125 characters. Do not start with phrases like "image of" or "picture of".' )
->generate_text();
if ( is_wp_error( $result ) ) {
return null;
}
update_post_meta( $attachment_id, '_ai_alt_text', $result );
return $result;
}
Bind the image block’s alt attribute to it the same way you tested a binding in the
previous lesson:
<!-- wp:image {"id":42,"sizeSlug":"large","metadata":{"bindings":{"alt":{"source":"my-plugin/ai-image-alt"}}}} -->
<figure class="wp-block-image size-large"><img src="https://example.com/wp-content/uploads/photo.jpg" alt="" class="wp-image-42"/></figure>
<!-- /wp:image -->
Step 4: Being honest about the read-only shape
register_block_bindings_source() has exactly two arguments that matter for behavior,
label and get_value_callback, plus the optional uses_context. There is no
update_value_callback argument, nothing in the registration itself lets an editor
write a new value back through this source. Once cached, the alt text this source
returns stays whatever it was the first time it was generated, until something else,
not the binding registration, clears or updates that cached meta value.
That “something else” has to be a separate mechanism you build yourself: a button in
the editor that calls a REST endpoint which deletes the cached _ai_alt_text meta
(forcing the next render to regenerate it), or a WP-CLI command an editor runs to
regenerate alt text for a batch of images. Lesson 5 and 6 build exactly that kind of
editor-triggered REST action, for a sidebar panel and a toolbar button respectively,
the same pattern applies here if you want a “Regenerate alt text” control rather than
relying purely on first-render generation.
Think of this source the way you’d think of a build step’s output, computed once, cached, and served after. If you need something closer to a live, editable connection, that has to be layered on top with your own REST endpoint and editor UI, the Block Bindings API by itself gives you the read side only.
Test it
Upload an image to the Media Library, insert an Image block referencing it, switch to
Code Editor, and add the metadata.bindings markup from Step 3 with the correct
attachment id. Switch back to the visual view. On first render, WordPress calls
my_plugin_get_ai_alt_text(), which calls the AI model and writes _ai_alt_text to
that attachment’s post meta:
wp-env run cli wp post meta get 42 _ai_alt_text
You should see the AI-generated description. Reload the post again, the value should come back instantly, confirming the cache is being read rather than a fresh AI call being made on every view.
Because the function only caches on success, a failed request (a missing API key, a provider outage) leaves the meta empty and triggers another AI call on the very next render, and the one after that, potentially hammering the provider’s API on a high-traffic page with no cached value yet. For production use, consider also caching a short-lived “generation failed, don’t retry for N minutes” marker so a persistent failure doesn’t turn into a request storm.
Recap
This source calls wp_ai_client_prompt() inside get_value_callback, generating an
image’s alt text from the attachment referenced in $block_instance->attributes['id'],
and caches the result as post meta so the expensive AI call happens once, not on every
page view. The Block Bindings API’s registration has no update_value_callback, so
anything resembling “regenerate this value” has to be built as a separate REST endpoint
and editor control, not as part of the source registration itself, exactly the kind of
editor UI the next few lessons cover.
Resources & further reading
- register_block_bindings_source() function reference, developer.wordpress.org
- Block Bindings API reference, developer.wordpress.org
- PHP AI Client SDK repository, GitHub