How to create a template for a custom post type in a block theme or plugin

There is finally a way to programmatically assign a template to a custom post type in WordPress. Most documentation online talks about how to create new templates either in the Full Site Editor or making custom templates for themes. Neither of these solutions work if you want to make a plugin that creates a custom post type with it’s own template.

WordPress 6.7 added a new function for registering these templates, register_block_template(). This new function allows us to declare a name, description, post type, and blocks used to make the template. We’ll talk more about those settings when we get to it.

A custom template declared for our Staff custom post type

Step 1. Ensure your custom post type is using blocks.

Before we can assign a template for our post type, we need to ensure its in the Gutenberg editor. To do this, find the register_post_type arguments and add “show_in_rest” and set it to true. Below is an example of this for a Staff custom post type that’s declared in your plugin.

function collinberg_register_cpt() {
      
 $args = array(
  'description'        => __( 'Description.', 'staff-direcotry-guide' ),
  'public'             => true,
  'show_in_rest' 	   => true, //this setting enables the block editor
  'publicly_queryable' => true,
  'show_ui'            => true,
  'show_in_menu'       => true,
  'rewrite'            => array('slug' => 'staff', 'with_front' => false),
  'capability_type'    => 'post',
  'has_archive'        => false,		
  'hierarchical'       => true,
  'menu_position'      => null,
  'menu_icon' 		   => 'dashicons-admin-users',
  'supports'           => array( 'title', 'editor', 'author', 'thumbnail','custom-fields'),
   //'template' 		=> '' //We're going to talk about the template setting later. It's only part of the solution.
  );

  register_post_type( 'staff', $args );

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

2) Make the HTML template file

Since this is a block plugin using Full Site Editor, we’re going to utilize an HTML template. If you were making a theme, you could just plop this down into your template folder and it would show up, but since we’re building this for a plugin, we need to register it first.

<!-- wp:template-part {"slug":"header","area":"header","tagName":"header"} /-->

<!-- wp:group {"tagName":"main"} -->
<main class="wp-block-group">
	<!-- wp:group {"layout":{"type":"constrained"}} -->
	<div class="wp-block-group">
		<!-- wp:spacer {"height":"var:preset|spacing|50"} -->
		<div style="height:var(--wp--preset--spacing--50)" aria-hidden="true" class="wp-block-spacer"></div>
		<!-- /wp:spacer -->

		<!-- wp:post-title {"textAlign":"center","level":1} /-->

		<!-- wp:spacer {"height":"var:preset|spacing|30","style":{"spacing":{"margin":{"top":"0","bottom":"0"}}}} -->
		<div style="margin-top:0;margin-bottom:0;height:var(--wp--preset--spacing--30)" aria-hidden="true"
			class="wp-block-spacer"></div>
		<!-- /wp:spacer -->

		<!-- wp:post-featured-image {"style":{"spacing":{"margin":{"bottom":"var:preset|spacing|40"}}}} /-->
	</div>
	<!-- /wp:group -->

	<!-- wp:post-content {"lock":{"move":false,"remove":true},"layout":{"type":"constrained"}} /-->
</main>
<!-- /wp:group -->

<!-- wp:template-part {"slug":"footer","area":"footer","tagName":"footer"} /-->

3) Register the template

Now, we want to register the file we just made and have it appear in the Full Site Editor templates section. To do this, we’ll call the register_block_template() function. The first parameter is the template name. The name should include the Plugin name, then //, then the template name itself. The plugin name will be used in the sidebar, to distinguish that it was this plugin, and not the theme, who declared it.

function staff_template_block_build($template){
  ob_start();
  include __DIR__ . "/templates/{$template}";
  return ob_get_clean();
}

add_action( 'init', 'staff_block_theme_template' );
function staff_block_theme_template() {
    register_block_template(
        'staff-directory-guide//staff-profile',
        array(
            'title'       => 'Single Staff',
            'description' => 'Displays a Staff Profile on your website unless a custom template has been applied to that post or a dedicated template exists.',
            'content'     => staff_template_block_build('single-staff.html'),
            'post_types'  => array( 'staff' ),
            )
    );
}

The next argument contains an array of options. The title declared is what is show to the user in the drop-down under page template, and on the template section of the site editor along with the description. Declaring a post type allows the template to load into the settings when you want to swap templates.

Now, to hook into the file we created, we’re going to write a quick small function that outputs the file sanitized, otherwise we’d have to paste the entire template file in. We’ll then call that function in the content argument, and select our single-staff file.

With that in place, we should be seeing our template show in the Site Editor templates section. and if we go to a staff post, we should be able to swap to that template. Now, we’ll hook into the default template hook for our custom post type, and have it select this template.

4) Setting new default template

At time of publishing, I haven’t found a way to manually set this template to default. The old way of hooking into the template hierarchy seems to not allow for checking the post type before setting a default for al singles. So for now, you’ll have to swap the template manually for each staff member. This isn’t an ideal approach so if you have a solution you can always email me [email protected]