Hybrid blocks & HTML API

Native WordPress blocks generally come in two forms: static or dynamic.

Static blocks store fixed HTML contents in the database, while dynamic blocks usually function as a placeholder in the editor, with the save callback typically returning null and rendering data dynamically on the front end.

What if we want to use both?

This still technically counts as a dynamic block, but since we want to save some content with the block rather than just a placeholder, I’ll call this a hybrid block.

Suppose you are refactoring a plugin which relies on the_content filter, basically adding some code or functionality not actually related to the “content” at all! The average user might not notice, but tacking on extra markup this way will likely have it showing up in your RSS feeds, and ActivityPub statuses! 😱

Anyhow, in classic themes we might have reached for a dynamic sidebar aka widget area in our single or singular template. In block themes, site editors can do this no-code!

Here is an example, we are saving the content of a RichText component:

export default function save({ attributes }) {
  const blockProps = useBlockProps.save();
  const logoAlt = "ShareOpenly logo";

  return (
    <div {...blockProps}>
      <img src={logo} alt={logoAlt} decoding="async" width="18" height="18" />
      &nbsp;
      <RichText.Content tagName="a" href="#" value={attributes.content} />
    </div>
  );
}

With this basic block saving the text content of a link, we can then override with a render_callback. Now, we can use the WP_HTML_Tag_Processor to dynamically customize our static content (the link’s href).

function share_openly_dynamic_block( $block_content ) {

  // Build the share_url query params
  global $wp, $post;
  $title = rawurlencode( esc_html( get_the_title() ) );
  $url = home_url( add_query_arg( array(), $wp->request ) );
  $share_url = add_query_arg( array(
    'url' => $url,
    'text' => $title,
  ), 'https://shareopenly.org/share/' );

  // Set the Share URL to the block content using the HTML API
  $processor = new WP_HTML_Tag_Processor( $block_content );
  if ( $processor->next_tag( 'a' ) ) {
    $processor->set_attribute( 'href', $share_url );
  }

  return $processor->get_updated_html();
}

This is a simple example, but the WP_HTML_Tag_Processor is quite powerful.

Another way a dynamic block might have used the HTML API would be to read the text from our block to do something else entirely for the render. The WP_HTML_Tag_Processor provides a method called get_modifiable_text().

As I noted in an, as of yet unpublished, User contributed note:

Since a #text node is not part of the tag itself, get_modifiable_text() can’t be used directly.
After selecting the desired tag you must first pass to the next_token().

function example_get_text_from_block( $block_content, $block ) {

  // $block_content = "<div>Lorem Ipsum</div>"
  $processor = new WP_HTML_Tag_Processor( $block_content );

  if ( $processor->next_tag( 'div' ) ) {
	$processor->next_token();
	$node_text = $processor->get_modifiable_text();
	error_log( $node_text ); // output: "Lorem Ipsum"
  }

  return $processor->get_updated_html();
}
add_filter( 'render_block_custom/div', 'example_get_text_from_block', 10, 2 )

Looking forward to WordPress 6.7 release, it looks like the complementary method set_modifiable_text() is on track to be included!

Have you used the HTML API recently?


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *