Contents
Overview
This article explains, in full detail, how to enforce content structure in the WordPress block editor by using template locking. It covers how template_lock works, where to set it (for post types and dynamically), examples in PHP, best practices, security and compatibility considerations, and troubleshooting steps. The content is written so you can copy the PHP snippets directly into a plugin or your themes functions.php and adapt them to your needs.
What is template_lock?
In the block editor environment, a template is an ordered list of blocks and block attributes that define the initial editing canvas for a post or custom post type. The template_lock property controls whether and how editors can change that template. Template locking is a way to enforce a content structure so authors can only edit the content of existing blocks, or cannot change them at all.
Common lock values and their semantics
- all — Fully locked. Users cannot insert, move, remove, or replace template blocks. This is used when the page structure must remain identical to the template. Authors can typically edit the inner content of editable blocks (like paragraph text) but cannot change the block layout.
- insert — Partially locked. Users cannot insert new blocks into the template structure, but can usually move or remove existing blocks depending on editor behavior. This is useful when you want to prevent new block insertion but still allow rearranging or editing existing elements.
- false or omitted — No lock. Editors can insert, move, remove, and replace blocks freely.
Where template_lock is used
- When registering a post type via register_post_type, by passing a template array and a template_lock key in the arguments.
- Dynamically through editor settings filters (for example, to lock only specific posts or to allow administrators to bypass locks).
Requirements and compatibility notes
- The template and template_lock features are part of the block editor and require the block editor to be enabled for the post type. That generally means supports => [editor] and show_in_rest => true when registering a post type.
- Make sure your environment uses a WordPress version that includes block templates and template_lock features (the block editor matured in recent major releases — test your target WP versions). If you need to support older WordPress versions, add conditionals and graceful fallbacks.
- Template locks are enforced by the block editor UI server-side validation of structure is recommended if strict enforcement is required beyond the editor UI.
How to define a template and template_lock for a custom post type
The typical approach is to register a post type and provide a template array with blocks and a template_lock value. Below is a canonical example. Put it in a plugin or a themes functions.php (preferably a plugin or a mu-plugin for portability).
lt?php add_action( init, function() { register_post_type( landing_page, array( label => Landing Pages, public => true, show_in_rest => true, // required for block editor templates to be used supports => array( title, editor, thumbnail ), // editor is required // Define a block template (list of blocks and attributes) template => array( // first block: a full-width hero heading array( core/cover, array( minHeight => 400, isDark => true, ), array( array( core/heading, array( level => 1, placeholder => Hero title... ) ), array( core/paragraph, array( placeholder => Subhead... ) ), ) ), // second block: a two-column row array( core/columns, array(), array( array( core/column, array(), array( array( core/heading, array( level => 2, placeholder => Column title ) ), array( core/paragraph, array( placeholder => Column text ) ), ) ), array( core/column, array(), array( array( core/heading, array( level => 2, placeholder => Column title ) ), array( core/paragraph, array( placeholder => Column text ) ), ) ), ) ), // a final call-to-action block array( core/buttons, array(), array( array( core/button, array( placeholder => CTA text ) ), ) ), ), // Lock the template to prevent adding/removing/reordering depending on value template_lock => all, // change to insert or remove for flexible editing ) ) } ) ?gt
Notes about the example
- show_in_rest must be true for the block editor to operate correctly for this post type.
- supports must include editor (the block editor) for the template to appear when editing posts of this type.
- template entries follow the structure: array( block-name, attributes_array, inner_blocks_array ).
Locking modes with examples
Fully locked (all)
Use all when you want authors to only fill in content inside blocks but not change the layout or block types.
lt?php // Example snippet — only the core code from the registration above: template_lock =gt all, ?gt
Partially locked (insert)
Use insert when you want to prevent adding new blocks to the template but allow some reordering or editing. This is useful when the structure is important but small rearrangements are acceptable.
lt?php template_lock =gt insert, ?gt
Dynamic locking: lock some posts or allow administrators to bypass the lock
You may want template locking only for a subset of posts, for posts with a meta flag, or to allow administrators to edit fully. The block editor accepts editor settings and one filter you can use is block_editor_settings_all to modify the editor initialization settings, including template-related settings. Below is an example that locks templates for posts with a meta key and allows administrators to bypass the lock.
lt?php add_filter( block_editor_settings_all, function( settings, post ) { // Only modify settings in the context of a post object (not the post-new screen) if ( empty( post ) ! isset( post->post_type ) ) { return settings } // Example condition: lock templates for landing_page posts with meta key _lock_template = 1 if ( landing_page === post->post_type ) { // Allow administrators to bypass the lock if ( current_user_can( manage_options ) ) { return settings } locked = get_post_meta( post->ID, _lock_template, true ) if ( 1 === locked ) { // Add or override the templateLock value used by the block editor settings[templateLock] = all } } return settings }, 10, 2 ) ?gt
Explanation
- The filter inspects the current post and modifies settings[templateLock] that the block editor uses.
- The sample respects capability checks so administrators are not unintentionally locked out.
- If you use this technique, ensure your logic runs early enough and that your post type uses the block editor.
Advanced: Server-side validation for stricter enforcement
Template locks in the editor are a client-side (editor UI) constraint enforced by JavaScript. If you need absolute structural integrity (for example, for layout-critical pages or for templates that are parsed on render), add server-side validation before saving post_content. Use filters such as wp_insert_post_data or the REST API hooks (if users edit through the REST API) to inspect and reject or sanitize invalid structures.
lt?php // Example: very simple check that ensures a required block exists in the post content. // This example checks for the presence of a block delimiter for a specific block type. // Production-quality validation should parse blocks with parse_blocks(). add_filter( wp_insert_post_data, function( data, postarr ) { // Only check specific post types if ( isset( postarr[post_type] ) landing_page === postarr[post_type] ) { // You can require a particular block, e.g. core/cover, be present in post_content if ( false === strpos( data[post_content],