Block Patterns are pre-built block compositions users can insert from the block inserter. In WordPress 6.3+ you can register patterns entirely in PHP, mark them as synced (pattern content is shared across all instances), and organize them into custom categories. Patterns are the modern replacement for shortcodes and widget-area layouts.
Problem: WordPress Full Site Editing themes ship with many built-in block patterns — but managing which patterns are shown in the inserter, creating synced vs unsynced variations, and overriding core patterns with custom designs is not well-documented.
Solution: Register patterns with register_block_pattern() and control visibility with the inserter, source, and blockTypes keys. Unregister unwanted core patterns with unregister_block_pattern(). Create synced patterns programmatically by inserting a wp_block post type entry. Curate the patterns directory by setting 'inserter' => false on internal layout patterns.
The code below registers a custom pattern category, a standard (unsynced) hero pattern, a synced pattern that acts like a reusable block, and shows how to restrict patterns to specific post types or block types.
__( 'My Theme', 'my-theme' ) ]
);
} );
// 2. Register an unsynced hero pattern
add_action( 'init', function () {
register_block_pattern(
'my-theme/hero-centered',
[
'title' => __( 'Centered Hero', 'my-theme' ),
'description' => __( 'A full-width hero with centred heading and CTA button.', 'my-theme' ),
'categories' => [ 'my-theme', 'featured' ],
'keywords' => [ 'hero', 'banner', 'header' ],
'blockTypes' => [ 'core/cover' ], // appears in "Transform to" for Cover blocks
'postTypes' => [ 'page', 'wp_template' ], // only offered on these post types
'content' => '
Your Headline Here
',
]
);
} );
// 3. Register a SYNCED pattern (acts like a reusable block, stored as wp_block CPT)
// Synced patterns are inserted by reference; editing one instance updates all.
add_action( 'init', function () {
// A synced pattern is a wp_block post — create it programmatically if missing
if ( ! get_page_by_path( 'global-cta-banner', OBJECT, 'wp_block' ) ) {
wp_insert_post( [
'post_type' => 'wp_block',
'post_title' => 'Global CTA Banner',
'post_name' => 'global-cta-banner',
'post_status' => 'publish',
'post_content' => '
',
'meta_input' => [ 'wp_pattern_sync_status' => '' ], // empty = synced
] );
}
} );
NOTE: Setting wp_pattern_sync_status meta to an empty string (or omitting it) marks a wp_block post as synced; setting it to 'unsynced' makes it a regular private pattern that is copied on insert — both are stored as wp_block posts.