How To Build WordPress Block Templates


In the Gutenberg era, the design process is not strictly tied to WordPress themes. Out of the box, the CMS provides users with all the design tools needed to build a great website layout and the theme aims to be something that adds even more building and design tools.

Block templates are a feature that unlocks even more powers in site building. According to the Block Editor Handbook:

A block template is defined as a list of block items. Such blocks can have predefined attributes, placeholder content, and be static or dynamic. Block templates allow specifying a default initial state for an editor session.

In other words, block templates are pre-built collections of blocks used to set a default state dynamically on the client.

👉 Block templates are different from Template files.

Template files are PHP files such as index.php, page.php, and single.php, and work the same way with both classic and block themes, according to the WordPress template hierarchy. In classic themes, these files are written in PHP and HTML. In block themes, they are entirely made of blocks.

👉 Block templates are different from Block patterns.

Block patterns need to be manually added to your pages while block templates automatically provide the initial layout and defaults when you or your team members create a new post.

You can also bind specific block templates to your custom post types and lock some blocks or features to force users to use your defaults or prevent errors.

You have a couple of ways to create block templates. You can use the block API to declare an array of block types via PHP, or you can create a custom block type using the InnerBlocks component.

Let’s dive in!

How To Build a Block Template Using PHP

If you’re an old-school developer, you can define a custom block template using a plugin or your theme’s functions.php. If you decide to go with a plugin, launch your favorite code editor, create a new PHP file, and add the following code:

<?php
/*
 * Plugin Name:       My Block Templates
 * Plugin URI:        https://example.com/
 * Description:       An example plugin
 * Version:           1.0
 * Requires at least: 5.5
 * Requires PHP:      8.0
 * Author:            Your name
 * Author URI:        https://author.example.com/
 * License:           GPL v2 or later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Update URI:        https://example.com/my-plugin/
 */

function myplugin_register_my_block_template() {
	$post_type_object = get_post_type_object( 'post' );
	$post_type_object->template = array(
		array( 'core/image' ),
		array( 'core/heading' ),
		array( 'core/paragraph' )
	);
}
add_action( 'init', 'myplugin_register_my_block_template' );

In the code above, get_post_type_object retrieves a post type by name.

Save your file in the wp-content/plugins folder, navigate to the Plugins screen in your WordPress dashboard, and activate the My Block Templates plugin.

Now, when you create a new post, the editor automatically launches your block template with an image block, a heading, and a paragraph.

A block template automatically loaded in the post editor
A block template automatically loaded in the post editor

You can also add an array of settings for each block and also create nested structures of blocks. The following function builds a more advanced block template with inner blocks and settings:

function myplugin_register_my_block_template() {

	$block_template = array(
		array( 'core/image' ),
		array( 'core/heading', array(
			'placeholder'	=> 'Add H2...',
			'level'			=> 2
		) ),
		array( 'core/paragraph', array(
			'placeholder'	=> 'Add paragraph...'
			
		) ),
		array( 'core/columns', 
			array(), 
			array( 
				array( 'core/column',
					array(),
					array(
						array( 'core/image' )
					)
				), 
				array( 'core/column',
					array(),
					array(
						array( 'core/heading', array(
							'placeholder'	=> 'Add H3...',
							'level'			=> 3
						) ),
						array( 'core/paragraph', array(
							'placeholder'	=> 'Add paragraph...'
						) )
					) 
				)
			) 
		)
	);
	$post_type_object = get_post_type_object( 'post' );
	$post_type_object->template = $block_template;
}
add_action( 'init', 'myplugin_register_my_block_template' );

You can see the output of the code above in the following image:

A more advanced block template
A more advanced block template

So far, we have only used core blocks. But you can also include custom blocks or block patterns in your block templates, as shown in the following example:

function myplugin_register_my_block_template() {
	$post_type_object = get_post_type_object( 'page' );
	$post_type_object->template = array(
		array( 'core/pattern', array(
			'slug' => 'my-plugin/my-block-pattern'
		) ) 
	);
}
add_action( 'init', 'myplugin_register_my_block_template' );

There’s not much difference in case you decide to create a default block template for an already registered custom post type. Simply change the post type of get_post_type_object to your custom post type name, as shown in the following example:

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

Now that you know how to create block templates, we can move forward and explore more use cases. Let’s dive a little deeper.

Block Templates With Custom Post Types

As we mentioned earlier, you can attach a block template to a custom post type. You can do that after your custom post type has already been registered, but you may prefer to define a block template on custom post type registration.

In this case, you can use the template and template_lock arguments of the register_post_type function:

function myplugin_register_book_post_type() {
	$args = array(
		'label' => esc_html__( 'Books' ),
		'labels' => array(
			'name' => esc_html__( 'Books' ),
			'singular_name' => esc_html__( 'Book' ),
		),
		'public' => true,
		'publicly_queryable' => true,
		'show_ui' => true,
		'show_in_rest' => true,
		'rest_namespace' => 'wp/v2',
		'has_archive' => true,
		'show_in_menu' => true,
		'show_in_nav_menus' => true,
		'supports' => array( 'title', 'editor', 'thumbnail' ),
		'template' => array(
			array( 'core/paragraph', array(
				'placeholder'	=> 'Add paragraph...'
			) ),
			array( 'core/columns', 
				array(), 
				array( 
					array( 'core/column',
						array(),
						array(
							array( 'core/image' )
						)
					), 
					array( 'core/column',
						array(),
						array(
							array( 'core/heading', array(
								'placeholder'	=> 'Add H3...',
								'level'			=> 3
							) ),
							array( 'core/paragraph', array(
								'placeholder'	=> 'Add paragraph...'
							) )
						) 
					)
				)
			)
		)
	);
	register_post_type( 'book', $args );
}
add_action( 'init', 'myplugin_register_book_post_type' );

And that’s it. The image below shows the block template in the editor’s interface for a Book custom post type.

A block template for a custom post type
A block template for a custom post type

When you are finished with the layout, you may want to play around with block settings to fine-tune the behavior and appearance of your block template.

Fine Tuning the Block Template With Block Attributes

We defined a block template as a list of blocks. Each item in the list should be an array containing the block name and an array of optional attributes. With nested arrays, you may want to add a third array for children blocks.

A template with a Columns block can be represented as follows:

$template = array( 'core/columns', 
	// attributes
	array(), 
	// nested blocks
	array(
		array( 'core/column' ),
		array( 'core/column' ) 
	) 
);

As mentioned above, the second array in the list is an optional array of block attributes. These attributes allow you to customize the appearance of your template so that you or your users can focus on the post content without caring about the page layout and design.

To start, you can use the block editor to create a structure of blocks you can use as a reference for your template.

A block layout in the block editor
A block layout in the block editor

Add your blocks, customize the layout and styles, then switch to the code editor and find block delimiters.

The block delimiter of a Columns block
The block delimiter of a Columns block

Block delimiters store block settings and styles in key/value pairs. You can simply copy and paste keys and values from the block markup to populate your array of attributes:

$template = array( 'core/columns', 
	array(
		'verticalAlignment'	=> 'center',
		'align'				=> 'wide',
		'style'				=> array( 
			'border'	=> array(
				'width'	=> '2px',
				'radius'	=> array(
					'topLeft'		=> '12px', 
					'topRight'		=> '12px', 
					'bottomLeft'	=> '12px', 
					'bottomRight'	=> '12px'
				)
			)
		),
		'backgroundColor' => 'tertiary'
	),
	array(
		array( 'core/column' ),
		array( 'core/column' ) 
	) 
);

Repeat the process for each block in the template and you are done.

$template = array(
	array( 'core/paragraph', array(
		'placeholder'	=> 'Add paragraph...'
	) ),
	array( 'core/columns', 
		array(
			'verticalAlignment'	=> 'center',
			'align'				=> 'wide',
			'style'				=> array( 
				'border'	=> array(
					'width'		=> '2px',
					'radius'	=> array(
						'topLeft'		=> '12px', 
						'topRight'		=> '12px', 
						'bottomLeft'	=> '12px', 
						'bottomRight'	=> '12px'
					)
				)
			),
			'backgroundColor' => 'tertiary',
			'lock' => array(
				'remove'	=> true,
				'move'		=> true
			)
		), 
		array( 
			array( 'core/column',
				array( 'verticalAlignment'	=> 'center' ),
				array(
					array( 'core/image', 
						array(
							'style'	=> array( 'border' => array( 'radius' => '8px' ) ) 
						) 
					)
				)
			), 
			array( 'core/column',
				array( 'verticalAlignment'	=> 'center' ),
				array(
					array( 'core/heading', array(
						'placeholder'	=> 'Add H3...',
						'level'			=> 3
					) ),
					array( 'core/paragraph', array(
						'placeholder'	=> 'Add paragraph...'
					) )
				) 
			)
		)
	)
);

Locking Blocks

You can lock specific blocks or all the blocks included in your template using the template_lock property of the $post_type_object.

Locking templates could be super useful when you have a multi-author blog and want to prevent all or specific users from changing the layout of your block template.

In the following example, we are locking all blocks in the block template:

function myplugin_register_my_block_template() {
	$post_type_object = get_post_type_object( 'post' );
	$post_type_object->template = array(
		array( 'core/image' ),
		array( 'core/heading' ),
		array( 'core/paragraph' )
	);
	$post_type_object->template_lock = 'all';
}
add_action( 'init', 'myplugin_register_my_block_template' );

Locked blocks show a lock icon in the block toolbar and list view:

A locked heading block
A locked heading block

Users can unlock blocks from the Options menu available in the block toolbar.

Unlocking a block
Unlocking a block

When you click on Unlock, a modal popup allows you to enable/disable movement, prevent removal, or both:

Locking options
Locking options

template_lock can take one of the following values:

  • all – Prevents users from adding new blocks, moving and removing existing blocks
  • insert – Prevents users from adding new blocks and removing existing blocks
  • contentOnly – Users can only edit the content of the blocks included in the template. Note that contentOnly can only be used at the pattern or template level and must be managed with code. (See also Locking APIs).
Setting template_lock to prevent template blocks from being removed
Setting template_lock to prevent template blocks from being removed

If you want to lock specific blocks, you can use the lock attribute on each block:

function myplugin_register_my_block_template() {
	$post_type_object = get_post_type_object( 'post' );
	$post_type_object->template = array(
		array( 'core/image' ),
		array( 'core/heading' ),
		array( 'core/paragraph', array(
			'lock' => array(
				'remove'	=> true,
				'move'		=> true
			)
		) )
	);
}
add_action( 'init', 'myplugin_register_my_block_template' );

The lock attribute can take one of the following values:

  • remove: Prevents users from removing a block.
  • move: Prevents users from moving a block.

And you can also use lock in conjunction with template_lock to fine-tune the behavior of the blocks included in your block template. In the following example, we are locking all blocks but the heading:

function myplugin_register_my_block_template() {
	$post_type_object = get_post_type_object( 'post' );
	$post_type_object->template = array(
		array( 'core/image' ),
		array( 'core/heading', array(
			'lock' => array(
				'remove'	=> false,
				'move'		=> false
			) 
		) ),
		array( 'core/paragraph' )
	);
	$post_type_object->template_lock = 'all';
}
add_action( 'init', 'myplugin_register_my_block_template' );

The image below shows the block template with locked and unlocked blocks:

A block template with locked and unlocked blocks
A block template with locked and unlocked blocks

Block developers can also use the lock attribute in the block definition on the attributes level (see also Individual block locking).