Define block templates for new pages, posts, and custom post types

WordPress’ block editor comes with the feature of block templates. This allows us to defined a set of blocks that will be present in the content of new pages, posts and/or custom post types. This post shows you how to use that functionality and how to use reusable blocks in templates.

What are block templates good for?

Let us assume, editors of a website need to insert the same blocks on almost all pages they create, for example, a group block containing a heading block.

For those cases, it is helpful to directly have these blocks in the content when creating the page, instead of the default paragraph block.

And for this, we have block templates.

Create a block template

Post type objects have an attribute template, that accepts an array of blocks.

To add a template to an existing post type, we hook into the init action and modify the wanted object. The code for an easy block template for pages can look like this:

/** * Add group block to new pages. * * @link */ function slug_post_type_template() { $page_type_object = get_post_type_object( 'page' ); $page_type_object->template = [ [ 'core/group', [], [ [ 'core/paragraph' ], ] ], ]; } add_action( 'init', 'slug_post_type_template' );
Code language: PHP (php)

Like said, we use the init action for our function. Inside the function, we get the post type object of pages and add a block array as the template.

This array contains one or more arrays for the first-level-blocks (in our case, a group block that is going to come with WordPress 5.3). With the second value of a block array, we could set attributes of the block, that could be the background and text color for the group block. We leave that empty for now.

The third value is an optional array of additional block arrays, which are added inside the parent block. Our example adds a paragraph block inside the group block. That works – of course – only for blocks that allow inner blocks.

Set block attributes

I already mentioned it: it is possible to set attributes of blocks with the second value of a block array. To set a background color for the group block, we pass the slug of a color that was added via an add_theme_support() call like described in the theme support reference on

/** * Add group block to new pages. * * @link */ function slug_post_type_template() { $page_type_object = get_post_type_object( 'page' ); $page_type_object->template = [ [ 'core/group', [ 'backgroundColor' => 'light' ], [ [ 'core/paragraph' ], ] ], ]; } add_action( 'init', 'slug_post_type_template' );
Code language: PHP (php)

Use reusable blocks in block templates

If you want to use a reusable block in block templates: that is possible, too. A reusable block has the type core/block and gets the ID of the block post for the ref attribute (reusable blocks are stored via a custom post type).

So we need the ID of the post and use it in the block template. That could look like this:

$unsere_leistungen_block = new WP_Query( [ 'post_type' => 'wp_block', 'title' => 'Services', ] ); if ( ! empty( $unsere_leistungen_block->posts ) ) { $page_type_object->template = [ [ 'core/block', [ 'ref' => $unsere_leistungen_block->posts[0]->ID] ], ]; }
Code language: PHP (php)

First, we search for the blocks with the title »Services« via a WP_Query, check if we got blocks and use the ID of the first block for the ref attribute of the core/block block we use in the template.

If the title of the block changes, you need to adjust the code.

Use block template for posts and custom post types

With the same procedure, we can define templates for posts and custom post types. For that, we adjust the parameter of the get_post_type_object() call, for example, to post instead of page, if we want to create a template for posts.

If you want to set a template for your own custom post type, you can do that directly when calling register_post_type() – the function accepts a template key in the parameter array, that gets the block arrays as a value.

An example of that and more can be found on the templates page in the block editor reference. There you can also see how to lock a template so that the user cannot remove or add blocks, or move them around.

17 reactions on »Define block templates for new pages, posts, and custom post types«

  1. Hello, is there a way to define a block template by prohibiting to insert a block before the template? Thank you for your tutorials, some of them have been very useful for me.

    1. Hi Ceylan,

      thanks for your comment, great to hear that my posts are useful 🙂

      To your question: Do I understand it correctly, that you want to prevent the user from inserting blocks before the template blocks, but allow blocks after the template? If that is correct, I do not know a way to do this, sorry.


      1. The most important thing for me is forbid the user to insert a block before the template. For the insertion of block after the template, it is less important.

          1. thanks for the answer, I had already watched Locking, but it's too restrictive. I am doing a wordpress template. I have a lot of template (custom post type etc.). For all the templates, I need to forbid the insertion before the template. Regarding insertion after, it depends on the template ...

  2. Thank you for the answer, I'll try this in the next few days. I have another question. Is it possible to make a template for a single page thanks to its id? Because the templates here are only possible thanks to the post type.

      1. I have not found a way to do it directly, but the bottom code seems to work more or less.

        function wp4378295342_maybe_add_template() {
            if ( ! is_admin() || ! isset( $_GET['post'] ) || '1234' !== $_GET['post'] ) {
                // This is not the post/page we want to limit things to.
                return false;
            $post_type_object = get_post_type_object( 'post' );
            $post_type_object->template = array(
                array( 'core/paragraph', array(
                    'placeholder' => 'Add Description...',
                ) ),
            $post_type_object->template_lock = 'all';
        add_action( 'init', 'wp4378295342_maybe_add_template' );
  3. Thanks for sharing this tutorial. Can you also please guide me how can I create a block template? I am doing the same using this resource but it’s giving an error and I am having some programming lines in front end. This is the code
    add_action( ‘init’, function() {
    $args = array(
    ‘public’ => true,
    ‘label’ => ‘News’,
    ‘show_in_rest’ => true,
    ‘template_lock’ => ‘all’,
    ‘template’ => array(
    array( ‘core/paragraph’, array(
    ‘placeholder’ => ‘Breaking News’,

  4. Hello,

    Is it possible to create, for example, a post type "Templates", create ready-made templates (like a page builder) and reuse these templates in new posts or pages?

    I tried to import using parse_blocks(), but I was not successful.

    Thanks in advance!

    1. Hi Vinicius,

      thanks for your comment. I am not sure if I fully understand what you mean. You want to create posts in the Templates post type that you can reuse as block templates for normal posts and pages?


  5. Hi Florian,

    Currently I'm working on a site that will have around 500 posts within a custom post type. I'd love to work with gutenberg and have a default template for all of them. Normally I'd do this with a single-custompostypename.php, but that prohibits me to use the Gutenberg for it.

    So is it possible to have a certain template get loaded with gutenberg blocks for all these 500 posts. But when I want to alter something (on all 500 of them), it changes it for all and not just for 1? Let's say I want to remove the email to display on all of them, can I just edit the gutenberg template?

    Would love to hear from you!

    1. Hi Klaas,

      you can use a single-cpt.php with Gutenberg, so altering the frontend template should be no problem.

      Or do you mean that you want to modify the included blocks for all CPT posts at once? I am not sure if that would be possible.


Leave a Reply

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