Full site editing templates for custom post types

Level: ,

Estimated reading time: 5 minutes

Several theme developers have reached out asking how to create full site editing templates for custom post types.

The main pain point is that developers and users are expecting to be able to create this type of template in the new Site Editor. This is a reasonable expectation. Unfortunately, the feature is not complete yet and not included in WordPress 6.0.

In this lesson I will describe how to create custom page templates and how to add default blocks to the content in the block editor.

Updated June 24, 2022.

File-based custom page templates

With custom page templates, I mean templates that are used by default when you view your post type on the front of your website. When you edit your custom post type in the block editor, the template is selected in the Templates section in the post settings sidebar:

The Template section in the post settings sidebar shows the current template and a list of available templates.

The blocks you add to this type of template are not visible in the block editor itself, only when viewing or editing the template.

The difference between creating a template file for a default post type and a custom post type is the naming of the file. If the name of your custom post type is “book,” you need to name the single template single-book.html.

  • Use an HTML file if you want the site editor to list the template.
  • Use a PHP template file if you need to:
    • Use conditionals
    • Display meta box options
    • Check if a plugin is active before adding a plugin block to the template

Remember to place HTML templates in the templates folder and PHP templates in the themes root folder.

You can read more in the official documentation in the theme developer handbook:

Adding default blocks to the block editor

Using register_post_type()

Plugins can register a template when registering a custom post type. This feature has been available since WordPress 5.0.
This method adds blocks directly in the block editor when you create a new post type item.

Since these two template methods do different things, it is a bit unfortunate that they share the same name! Depending on what type of project you are working on, you would use both methods together.

The template argument in the PHP function register_post_type() accepts blocks and block settings in the form of a nested array:

function myplugin_register_book_post_type() {
    $args = array(
        'public' => true,
        'label'  => 'Books',
        'show_in_rest' => true,
        'template' => array(
            array( 'core/columns', array(), array(
                array( 'core/column', array(), array(
                    array( 'core/image', array() ),
                ) ),
                array( 'core/column', array(), array(
                    array( 'core/paragraph', array(
                        'placeholder' => 'Add a inner paragraph'
                    ) ),
                ) ),
            ) )
    register_post_type( 'book', $args );
add_action( 'init', 'myplugin_register_book_post_type' );

Filtering the template argument for existing custom types

You can update the template argument of an existing custom post type by using a filter:

function myplugin_register_template() {
    $post_type_object = get_post_type_object( 'book' );
    $post_type_object->template = array(
        array( 'core/image' ),
add_action( 'init', 'myplugin_register_template' );

Troubleshooting tips

In my experience, adding these nested arrays correctly can be a bit difficult.
The array structure is the same as for a parsed block. The block name is a string, while the attributes and the inner blocks are arrays.
To find out which attributes a block supports, look through the block reference or the block’s block.json file.

If you see errors in your template, try adding the block in the block editor, copy the markup, and run it through parse_blocks(). Print the result of parse_blocks() and compare the array with your template.

array ( 
    0 => array ( 
        'blockName' => 'core/paragraph', 
        'attrs' => array ( 
            'textColor' => 'vivid-red', 
            'backgroundColor' => 'luminous-vivid-amber',
        'innerBlocks' => array (
            /* Child blocks if they exists (used in Column Block for example) */

Adding default blocks with the default_content filter

Another way to display default blocks in the editor when you create a new post type item is to filter the content. You can read about the default_content filter in the WordPress code reference:

In this code example I am first checking if the post type is book, before adding the block markup to the $content variable.
I am adding the same content as in the previous example, a columns block with an image and a paragraph:

function prefix_filter_book_content( $content, $post ) {
	if ( $post->post_type === 'book' ) {
		$content ='<!-- wp:columns -->
		<div class="wp-block-columns"><!-- wp:column -->
		<div class="wp-block-column"><!-- wp:image -->
		<figure class="wp-block-image"><img alt=""/></figure>
		<!-- /wp:image --></div>
		<!-- /wp:column -->

		<!-- wp:column -->
		<div class="wp-block-column"><!-- wp:paragraph -->
		<!-- /wp:paragraph --></div>
		<!-- /wp:column --></div>
		<!-- /wp:columns -->';
	return $content;
add_filter( 'default_content', 'prefix_filter_book_content', 10, 2 );

– You can use other conditionals, for example checking for a specific author or post title.

Here I have created a new book item through the WordPress admin area, and the block editor have placed the selected blocks for me:

The block editor shows the book custom post type with the default columns block.

Further reading

Gutenberg GitHub issues related to full site editing templates for custom post types:
Expand the types of templates that can be added in the Site Editor
Create Block Template from copied block content
Using Patterns in Templates

Preview of templates for WooCommerce: https://developer.woocommerce.com/2021/11/22/woocommerce-blocks-6-3-2-release-notes/