WordPress Block Templates

WordPress block templates are part of the block editor workflow, used to keep content structured and consistent as new posts, pages, or post types are created. Once you start working on anything beyond a small website, maintaining that consistency becomes harder. Different authors format things their own way, skip sections, and let layouts drift.

Block templates provide a starting point in the block editor, so content doesn’t start from scratch every time. Rather than relying on memory or guidelines, the structure is already in place when the editor opens. That alone changes how content gets written, especially in teams.

This article focuses on how WordPress block templates fit into that process: what they are, how they’re set up, how they’re controlled, and how they connect to other parts of block-based editing. The goal is to make sense of how they actually behave in real use, not just how they’re described.

What Are WordPress Block Templates

A WordPress block template is a predefined list of block items that sets the default initial state of the editor session. When you open a new post in the block editor, instead of starting empty, you’re looking at a structured layout already in place: headings, paragraphs, maybe a few placeholders, whatever has been defined in that template.

In simple terms, block templates describe what the editor should look like before creating a new layout or working on content. They don’t directly control how the page appears on the front end. They define the starting point inside the editor. That’s an important distinction, because it shifts the focus from output to workflow.

This is where they differ from classic templates. Classic templates are PHP files tied to how content is displayed on the site, usually handled at the theme level. Block templates, on the other hand, represent a structured list of blocks used by the editor itself. Same word “template” but a completely different layer. One is about presentation, the other about the editing experience.

If you’ve worked with WordPress Gutenberg before, you’ve seen this behavior. When a new post is created in the block editor, WordPress checks the assigned template for that post type and loads its predefined block structure into the editor. That’s why some post types open with a layout already in place; the template is applied automatically during the editor initialization.

Another thing worth noting: block templates apply only when content is first created. They don’t overwrite or reshape existing posts. Once something is saved, the template steps back, and the content stands on its own. That keeps things predictable and avoids breaking anything that’s already live.

How Block Templates Work

Block templates work by describing a layout as a PHP array that the block editor can read and turn into actual blocks. Each block template is essentially a structured list, where every item points to a specific block and how it should appear when the editor first opens. 

When a new post is created, WordPress reads that template and uses it to populate the editor session. Instead of loading a blank canvas, the editor fills in the predefined structure right away. That process happens automatically in the background, tied to the post type, so the moment you click “Add New,” the layout is already there waiting.

At a basic level, every block template consists of three parts. There’s the block name, which tells the editor what kind of block to insert. Then come the attributes, which adjust how that block behaves or looks, things like alignment or placeholder text. And finally, there are inner blocks, which allow one block to contain others, forming a nested structure. 

If you’ve spent any time around the WordPress full site editor, the idea feels familiar. The system relies on structured data to describe layouts, and block templates follow the same logic, just focused on the editor’s starting state rather than the entire site.

Template Structure and Syntax

A block template is structured as a PHP array, where each item in that array represents a block and its initial setup inside the editor. It’s a simple pattern once you see it: an array of arrays, each one describing a single block in order.

Here’s a minimal example of how that looks:

$template = [
   [ 'core/heading', [ 'level' => 2 ], [] ],
   [ 'core/paragraph', [], [] ],
];

The template array contains block items, each of which follows the same three-part structure. First comes the block name. This always follows a namespace format, like core/heading or core/paragraph, where core is the namespace and the second part is the block itself. That naming pattern matters because it tells the editor exactly which block to load.

Next is the attributes array. This is an associative array, even if it’s empty. In the heading example, the level => 2 sets it as an H2 by default. Sometimes you’ll leave this part blank, sometimes not. It depends on how much control you need over the starting state.

The third part is for inner blocks. That’s another array, used when a block can contain other blocks inside it. In this basic example, it’s empty, but in more complex templates, this is where nesting begins.

If you’ve worked with the template hierarchy before, the idea of structured layers might feel familiar. The difference here is that everything stays focused on the editor’s starting layout, not the final output.

Block Attributes in Templates

Block attributes shape each block within a block template. They sit right in the middle of the array, written as simple key-value pairs, and that’s where most of the small adjustments happen. Not the structure itself, but the details. How a block looks, how it behaves when someone starts typing.

Here’s a quick example so it’s easier to see what that actually means:

$template = [
   [ 'core/heading', [ 'level' => 2, 'className' => 'section-title' ], [] ],
   [ 'core/paragraph', [ 'placeholder' => 'Write something...', 'align' => 'left' ], [] ],
];

Each block has its attributes tucked into that second array. The level on the heading is straightforward; it sets the heading size before anything is written. className just adds a CSS class, nothing fancy, but it’s useful when you want consistent styling without fixing things later.

The paragraph block shows a couple of different ones. A placeholder is that faint text you see before typing. It’s not essential, but it helps guide whoever’s editing, especially if the layout has a specific purpose. Then there’s align. Left, center, right, depends on the block, though. Not all of them support the same options, which can be a bit annoying until you get used to it.

That’s kind of the thing with attributes in general. They’re predictable once you’ve used them a few times, but there isn’t a universal set. A heading won’t care about the same values as a paragraph, and some blocks have their own quirks.

Creating Block Templates with PHP

Creating a block template in PHP is about placing that array where WordPress expects it. The format itself doesn’t change; you’re still working with the same list of blocks, but the way you attach it depends on the situation.

If you’re dealing with an existing post type, you don’t start from scratch. You usually hook into it through something like register_post_type_args. It’s more of an adjustment than a setup. You’re telling WordPress, “when this editor opens, use this structure.” 

Now, if you’re defining a post type yourself, it’s a bit more direct. The block template goes into the register_post_type arguments right away. Same array, same logic, just declared earlier instead of patched in later. 

Most of this ends up in functions.php unless you prefer to keep it in a plugin. Either works. If you’ve touched PHP for WordPress before, this won’t feel new. It’s just another parameter being passed along with the rest.

Adding Templates to Existing Post Types

The register_post_type_args filter is basically how you sneak a template into something that already exists, like regular posts. You’re not rebuilding the post type or anything like that. You catch its arguments at the right moment and add what you need.

It works through a callback. You get the $args, you get the $post_type, and then you decide if it’s the one you care about. If it is, you drop your template into $args[‘template’] and send everything back. Pretty straightforward once you’ve done it once or twice.

Here’s a simple version:

function add_template_to_posts($args, $post_type) {
   if ($post_type === 'post') {
       $args['template'] = [
           [ 'core/heading', [ 'level' => 2 ], [] ],
           [ 'core/paragraph', [ 'placeholder' => 'Write something here...' ], [] ],
       ];
   }
   return $args;
}
add_filter('register_post_type_args', 'add_template_to_posts', 10, 2);

That check in the middle matters. Without it, you’d end up attaching the same template everywhere, which usually isn’t what you want. This way, it stays tied to just posts.

Once it’s in place, you’ll notice it right away. Hit “Add New,” and instead of a blank editor, you get a heading sitting there, plus a paragraph underneath. Ready to go. No setup needed each time, which is kind of the whole point.

If you’ve looked into something like how to create a custom post type before, the pattern feels familiar. Same idea of working with post type arguments, just handled through a filter instead of defining everything upfront.

One small thing that’s easy to miss: this only affects new posts. Anything that’s already saved stays as it is. The template doesn’t go back and rearrange old content, which is actually a good thing most of the time.

Registering Custom Post Types with Templates

When you’re registering a custom post type, the template argument goes straight into register_post_type, so everything is defined in one place. Unlike tweaking an existing type with a filter, here you’re setting the rules from the beginning. The structure is the same, just passed as part of the post type arguments instead of being added later.

The template argument accepts a block template array, along with a few related settings. Here’s a basic example of a custom post type with a template included:

function register_book_post_type() {
   register_post_type('book', [
       'label' => 'Books',
       'public' => true,
       'show_in_rest' => true,
       'template' => [
           [ 'core/image', [], [] ],
           [ 'core/heading', [ 'level' => 2 ], [] ],
           [ 'core/paragraph', [ 'placeholder' => 'Add book description...' ], [] ],
       ],
       'template_lock' => 'all',
   ]);
}
add_action('init', 'register_book_post_type');

Here, register_post_type includes the template directly, so every new “book” entry opens with that layout already in place. The custom post type defines its structure up front: image first, then a heading, then a paragraph. 

The template argument uses the same array format as before. No changes there. The template_lock setting is included as well, though it’s just a preview of what’s possible. You can control how strict the layout is, but that’s a separate topic on its own.

One small thing that’s easy to miss: show_in_rest needs to be set to true. Without it, the block editor won’t load, and the template won’t apply at all. It’s a simple flag, but it makes the whole setup work.

Locking Block Templates

Template locking is how block templates handle editorial control once the layout is in place. It’s not about security or permissions; it’s more about guiding content editing to keep the structure from drifting over time, especially on sites where more than one person is publishing, which matters more than you’d think.

Block templates support locking to control what can be changed inside the editor. Without it, the initial layout is just a suggestion. Anyone can move things around, remove sections, or add new blocks wherever they want. Sometimes that’s fine. Other times, it slowly breaks the consistency you were trying to keep.

Locking helps keep things in line. It sets boundaries around the structure, so the important parts stay where they should. This is useful for things like article layouts, landing pages, or any format where order actually matters. Not rigid, just a bit more controlled.

There are two levels to it. Template-level locking applies to the whole structure, setting rules for the entire layout at once. Then there’s individual block locking, which works at a smaller scale: specific blocks can be restricted while others stay flexible. That mix is usually what makes it practical.

Template-Level Locking Options

The template_lock property controls how much freedom you actually leave within a block template. You set it once, next to the template itself, and from that point on it quietly decides what can and can’t be changed in the layout. 

There are three values you can use, and they each feel a bit different when you try them:

Lock ValueBehaviorUse Case
allStops moving, removing, and adding blocks completelyWhen the layout has to stay exactly as designed
insertLets you move or remove blocks, but blocks adding new onesWhen structure matters, but not every detail is fixed
contentOnlyKeeps the layout in place but allows editing inside each blockWhen text changes, but structure shouldn’t shift

The template_lock property with all is the strictest one. Once it’s applied, the layout just sits there: no dragging things around, no deleting sections, no adding extras. It’s useful, but can feel a bit rigid if you overuse it.

insert is usually easier to live with. You can still rearrange or remove blocks if needed, but you won’t end up with random extras breaking the layout. That balance tends to work better on most sites.

Then there’s contentOnly, which is a bit different. It doesn’t care about the text itself, just the structure. Blocks stay in place, but you can still edit what’s inside them. Good fit for structured content where order matters more than wording.

Here’s what it looks like when you actually set it:

register_post_type('book', [
   'label' => 'Books',
   'public' => true,
   'show_in_rest' => true,
   'template' => [
       [ 'core/heading', [ 'level' => 2 ], [] ],
       [ 'core/paragraph', [], [] ],
   ],
   'template_lock' => 'insert',
]);

In this case, new entries follow the layout, but there’s still room to move things around a bit. No extra blocks can sneak in, though.

Picking the right option usually comes down to how strict you want to be. Too loose, and the layout drifts. Too strict, and editing becomes annoying. Somewhere in between is usually where it lands.

Individual Block Locking

Beyond locking the whole layout, a block template can handle things one block at a time. That’s usually where it starts to feel usable. You don’t always want everything locked down; just a few key pieces that shouldn’t move or disappear.

The lock attribute is what makes that possible. It lives inside the block’s settings and uses two simple flags: move and remove. Both are either true or false. Set one to false, and that action is off the table. Leave it alone, and the block behaves like normal.

Here’s a quick example with a bit of variation:

$template = [
   [ 'core/heading', [ 'level' => 2, 'lock' => [ 'move' => false, 'remove' => false ] ], [] ],
   [ 'core/paragraph', [ 'placeholder' => 'Start typing...' ], [] ],
   [ 'core/paragraph', [ 'lock' => [ 'remove' => false ] ], [] ],
];

The heading is fixed in place. You can’t drag it somewhere else, and you can’t delete it either. It just stays there. The first paragraph is completely open, nothing restricted. The second one is a bit stricter: you can move it if you want, but removing it isn’t allowed.

That kind of setup comes up a lot. A title that stays put, a required section that shouldn’t disappear, and then some space where things can shift around a bit depending on the content.

In the editor, you’ll notice a small lock icon on blocks that have restrictions. It shows up in the block toolbar. Not super loud, but it’s there. Once you try to move or remove something and it doesn’t work, it makes sense pretty quickly.

This also plays alongside the broader template rules. If the whole layout is already restricted, these settings don’t really loosen anything. But if the template is more open, per-block locking gives you a bit more control without making everything rigid.

Nested Templates and Advanced Patterns

Nested templates arise from using the third piece of the structure: the inner blocks array. That’s the part where things stop being just a list and start taking shape as an actual layout. You’re not just adding blocks anymore, you’re placing them inside other blocks.

A common case is columns. You’ve got one parent block, and then everything else sits inside it. It sounds more complicated than it is, but once you see it, it clicks pretty fast.

$template = [
    [
        'core/columns',
        [],
        [
            [
                'core/column',
                [],
                [
                    [ 'core/heading', [ 'level' => 3 ], [] ],
                    [ 'core/paragraph', [], [] ],
                ]
            ],
            [
                'core/column',
                [],
                [
                    [ 'core/heading', [ 'level' => 3 ], [] ],
                    [ 'core/paragraph', [], [] ],
                ]
            ],
        ]
    ],
];

So what’s going on here? The core/columns block sits at the top, then each core/column is nested inside it, and each column has its own blocks. It’s basically layers of the same pattern repeating.

There’s also a shortcut people use when templates start getting long. Instead of writing everything out, you can reference a saved pattern using its slug. That’s where the Pattern block comes in.

$template = [
   [ 'core/pattern', [ 'slug' => 'my-theme/two-column-layout' ], [] ],
];

That way, you keep the template cleaner and reuse layouts without copying them everywhere. Makes maintenance easier, too, especially if something changes later.

Block Templates vs Block Themes vs Block Patterns

Block templates, block themes, and block patterns are often mixed up, mostly because they all deal with layout in one way or another. They sit in the same space, but they don’t do the same job. The difference mostly comes down to where they operate and how they’re used.

Block templates work at the post-type level. They define how the editor starts when you create new content. Block themes move at a broader level. They shape how the entire site is structured and rendered. Patterns sit elsewhere entirely, more like reusable pieces you insert as needed.

Here’s a quick side-by-side view:

FeatureBlock TemplatesBlock ThemesBlock Patterns
DefinitionPredefined block layout for editor startTheme built with block-based templatesReusable group of blocks
ScopePost-type levelSite levelInserted per use
How CreatedPHP arraysHTML template files with block markupDefined patterns (PHP or JSON)
EditabilityApplied automatically on new contentEditable through theme structureManually inserted and edited
Use CaseConsistent content structureOverall site layout and designRepeating sections (e.g. CTAs, grids)

Block templates stay focused on the editor’s starting point. They apply automatically, and only when new content is created. No manual step is involved. That’s what makes them useful for maintaining consistent structure across posts or pages.

A WordPress theme, especially a block theme, works at the site level instead. It defines how templates, layouts, and visual structure come together across the whole site. Different layer, different responsibility.

Patterns are more hands-on. You pick them from the inserter, drop them into the page, and adjust as needed. They don’t load automatically, and they don’t control the editor’s initial state.

All three belong to the same WordPress block ecosystem, just at different levels. Templates handle how content begins. Themes shape how everything fits together. Patterns fill in repeatable pieces along the way.

More Articles by Topic
Every URL within a WordPress website ultimately maps to a particular template file. WordPress uses a predefined sequence, known as…
Learn more
The WordPress Administrator role controls every aspect of a single-site WordPress installation. Content publishing, plugin management, theme configuration, user accounts,…
Learn more
Gutenberg replaced the Classic Editor as WordPress's default editor in version 5.0, released in December 2018. Every WordPress installation since…
Learn more

Contact

Feel free to reach out! We are excited to begin our collaboration!

Don't like forms?
Shoot us an email at [email protected]
CEO, Strategic Advisor
Reviewed on Clutch

Send a Project Brief

Fill out and send a form. Our Advisor Team will contact you promptly!

    Note: We will not spam you and your contact information will not be shared.