Update: an improved solution has been suggested in the comments. Do read the article but then skip to the comment to find a newer solution.

Update: after some research it appears it is not currently possible to use block grammar in the template of a post. Therefore the contents of this post should be treated as experimental and will not work in production. Hopefully in the future we can update Gutenberg to support the suggested approach.

WordPress allows you to provide a default block template (defined as a list of block items) for a given post. As such Block templates can be used to specify a default initial state for an editor session. This has been written about many times elsewhere and is particularly handy if you want to provide a default editor template for a particular post type you are creating.

Aside: an alternative to this approach is to embrace the new Template Editor.

Currrently the API for static editor block templates requires the developer to pass an an array of block types (block name, attributes and innerBlocks) and set this as the template property of the post object in question.

For example:

<?php
function myplugin_register_template() {
    $post_type_object = get_post_type_object( 'post' );
    $post_type_object->template = array(
        array( 'core/image' ),
    );
}
add_action( 'init', 'myplugin_register_template' );

Unfortunately however, authoring templates using this array-based format is a little clunky and is also prone to error.

Wouldn’t it be nice to be able to create your template in the editor, click Copy all Content and then pass the resulting block grammar as the template property and have WordPress handle the conversion? Well unfortunately, this isn’t currently possible out of the box.

Creating templates from Block Grammar

As mentioned above, the template property of the post object expects an array of blocks and thus will not accept raw block grammar.

What we need then is some way to convert the blocks to the expected format.

Enter parse_blocks, which is WordPress’s PHP implementation of the block parser which will parse any block objects out of a given string of content.

Using this technique we can do something like this to convert copied blocks into the array format required by the template property:

function myplugin_register_template() {
	$post_type_object = get_post_type_object( 'post' );
		// Parse the blocks
	$blocks = parse_blocks( '<!-- wp:separator {"className":"is-style-default"} --><hr class="wp-block-separator is-style-default"/><!-- /wp:separator -->' );

		// Convert to required format
	$block_template = array_map(
		function( $block ) {
			return array(
				$block['blockName'],
				array_merge(
					$block['attrs'],
					array(
						'content' => $block['innerHTML'],
					),
				),
				$block['innerBlocks'],
			);
		},
		$blocks
	);

	$post_type_object->template = $block_template;

}
add_action( 'init', 'myplugin_register_template' );

Unfortunately however, this doesn’t fully work as we’re only re-mapping/converting the topmost blocks in the array. Any innerBlocks will not be converted to the correct format.

We can solve this with a tiny bit of recursion and a dash of refactoring.

Recursing and handling innerBlocks

First we create a dedicated function which will recursively convert a given block object (including it’s innerBlocks) into the array format:

function convertBlockToTemplate( $block ) {
	$template = array(
		$block['blockName'],
		array_merge(
			$block['attrs'],
			array(
				'content' => $block['innerHTML'],
			),
		),
	);

    // Optionally recurse into innerBlocks if found
	$template[] = ( ! empty( $block['innerBlocks'] ) ) ? array_map( 'convertBlockToTemplate', $block['innerBlocks'] ) : array();

	return $template;
}

Notice how if there are innerBlocks present, we call convertBlockToTemplate() again, thus ensuring we recurse through all nested levels of the innerBlocks.

With that in place we can create a simple function which accepts a pattern and then processes it into the format required by template by calling convertBlockToTemplate:

function buildTemplateFromPattern( $pattern ) {

	$pat = str_replace(
		array( "\r", "\n" ),
		'',
		$pattern,
	);

	$blocks = parse_blocks( $pat );

	$block_template = array_map(
		'convertBlockToTemplate',
		$blocks
	);

	return $block_template;
}

As most block content tends to come with new lines, we’re also stripping these output prior to parsing.

Now all we need do is assign the result of calling this function with some block content to the template property of the post object:

function myplugin_register_template() {
	$post_type_object = get_post_type_object( 'post' );

	$post_type_object->template = buildTemplateFromPattern( SOME_PATTERN );

}
add_action( 'init', 'myplugin_register_template' );

Passing any valid block content string in place of SOME_PATTERN will result in the template being correctly converted and displayed in the editor when visiting Posts -> New Post.

Using the Pattern Directory

WordPress now has a pattern directory which provides an excellent source of block content to plug into our new templating solution.

Simply visit any pattern and click Copy Pattern. Now pass the resulting string into the buildTemplateFromPattern() function and voila – the pattern will appear in your editor!

Setting a Block Pattern as the default Post Editor Template

Hopefully the recent addition of the Template Editor to WordPress 5.8 will soon make such default templates obsolete but in the meantime this solution should be useful.

Props to Martin Jost for prompting me to look into this issue. I hope you find it useful.

5 responses to “Setting default WordPress Editor Block Templates via PHP from Block Grammar

  1. Hi Dave,
    I tried the code proposed here on a site under development (wordpress 5.9 beta with gutenberg) and it seems to work very well.

    Too bad it can’t be used but thanks anyway for your nice solution.

    Best regards,
    Flavio Masi

  2. Colin says:

    This has been improved in WP 5.9. There is now a ‘core/pattern’ block that takes a pattern slug as an attr. To use it, copy/paste your HTML from the editor and register it as a Block Pattern:
    register_block_pattern( ‘my_theme/custom_post_type_template’, array( ‘content’ => {html copied from editor} ) );
    Then register a template with the pattern as the only block:
    $post_type_object = get_post_type_object( ‘custom_post_type’ );
    $post_type_object->template = array(
    array(
    ‘core/pattern’,
    array(
    ‘slug’ => ‘my_theme/custom_post_type_template’ )
    ),
    );

    1. Dave Smith says:

      Great solution. Thank you so much for taking the time to provide it here.

  3. Lee Lemon says:

    Hey, this was a great solution. But as Colin stated is no longer needed for updated wordpress sites. I spent a few days trying to get this to work before I found this page and finally chose to use Colin’s comment.
    You may want to modify your article to include his solution as it is easier to implement. But great job and solution for anyone using the older versions.

    1. Dave Smith says:

      Thanks Lee. No problem – glad it was (sort of) useful.

      I’ve updated the post at the top with a notice to check out Colin’s comment.

Leave a Reply to Flavio Masi Cancel reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.