How to Create Custom Block Patterns in WordPress Block Themes
Block patterns are one of the most powerful features in the WordPress block theme ecosystem. They are pre-built layouts made of blocks that you can insert with a single click: hero sections, pricing tables, testimonial grids, call-to-action bars, team member layouts, and anything else you can build with blocks. Instead of assembling the same layout manually every time, you build it once as a pattern and reuse it across your entire site.
WordPress ships with dozens of default patterns, but the real power comes from creating your own. Custom patterns let you define your brand’s specific layouts, enforce design consistency across your site, and give content editors pre-approved sections they can drop into any page without touching the design.
This guide covers everything: registering patterns in PHP, organizing them into categories, understanding synced vs unsynced patterns, creating patterns from the editor, pattern locking, and building advanced PHP-based dynamic patterns. If you are new to block themes, start with our complete theme.json configuration guide first.
What Block Patterns Are (and Are Not)
A block pattern is a collection of blocks arranged in a specific layout with predefined content, styles, and settings. When you insert a pattern into a page, WordPress copies all the blocks and their attributes into your content. From that point, you can edit the blocks individually; the pattern is just the starting point.
Patterns are not reusable blocks (now called synced patterns). The difference matters:
| Feature | Regular Pattern (Unsynced) | Synced Pattern |
|---|---|---|
| What happens on insert | Blocks are copied. Each instance is independent. | A reference is inserted. All instances share the same content. |
| Editing one instance | Only affects that instance | Changes appear everywhere the pattern is used |
| Best for | Starting layouts, templates, design systems | Shared content like CTAs, announcements, footer blocks |
| Stored as | PHP registration or wp_block post type | wp_block post type with wp_pattern_sync_status meta |
Method 1: Register Patterns in PHP
The most reliable way to add custom patterns to your block theme is through PHP registration using the register_block_pattern() function. This approach keeps patterns in your theme’s codebase, makes them version-controllable, and ensures they are available immediately on theme activation.
Basic Pattern Registration
add_action( 'init', function() {
register_block_pattern(
'mytheme/hero-with-cta',
array(
'title' => __( 'Hero Section with CTA', 'mytheme' ),
'description' => __( 'A full-width hero section with heading, paragraph, and two buttons.', 'mytheme' ),
'categories' => array( 'featured', 'banner' ),
'keywords' => array( 'hero', 'banner', 'cta', 'landing' ),
'content' => '
<div class="wp-block-cover alignfull"><span class="wp-block-cover__background has-black-background-color has-background-dim-60 has-background-dim"></span><div class="wp-block-cover__inner-container is-layout-flow wp-block-cover-is-layout-flow">
<h1 class="wp-block-heading has-text-align-center" style="font-size:3.5rem">Build Something Beautiful</h1>
<p class="has-text-align-center wp-block-paragraph">Create stunning websites with the power of WordPress block themes.</p>
<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-3e41869c wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button">Get Started</a></div>
<div class="wp-block-button is-style-outline is-style-outline--1"><a class="wp-block-button__link wp-element-button">Learn More</a></div>
</div>
</div></div>
',
)
);
});
Pattern Properties Explained
| Property | Required | Purpose |
|---|---|---|
title | Yes | Display name in the pattern inserter |
content | Yes | Block markup (copy from the editor’s Code Editor view) |
description | No | Shown as a tooltip in the pattern inserter and read by screen readers |
categories | No | Array of category slugs for organization |
keywords | No | Search terms to help users find the pattern |
viewportWidth | No | Preview width in pixels (default: 800) |
blockTypes | No | Suggest this pattern when specific blocks are inserted |
postTypes | No | Restrict the pattern to specific post types |
inserter | No | Set to false to hide from the inserter (for programmatic use only) |
Method 2: File-Based Patterns (WordPress 6.0+)
Since WordPress 6.0, you can register patterns by simply creating PHP files in your theme’s patterns/ directory. WordPress automatically discovers and registers them; no register_block_pattern() call needed.
Create the Pattern File
Create a file at patterns/hero-cta.php in your theme:
<?php
/**
* Title: Hero Section with CTA
* Slug: mytheme/hero-cta
* Categories: featured, banner
* Keywords: hero, banner, cta, landing
* Viewport Width: 1200
*/
?>
<!-- wp:cover {"dimRatio":60,"overlayColor":"black","isUserOverlayColor":true,"align":"full"} -->
<div class="wp-block-cover alignfull">
<span class="wp-block-cover__background has-black-background-color has-background-dim-60 has-background-dim"></span>
<div class="wp-block-cover__inner-container">
<!-- wp:heading {"textAlign":"center","level":1} -->
<h1 class="wp-block-heading has-text-align-center">Build Something Beautiful</h1>
<!-- /wp:heading -->
<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Create stunning websites with WordPress block themes.</p>
<!-- /wp:paragraph -->
</div>
</div>
<!-- /wp:cover -->
The PHP comment block at the top is the pattern header. WordPress reads these comments to register the pattern automatically. The Slug must be unique and should follow the themename/pattern-name convention.
This method is cleaner than PHP registration for most use cases. Your pattern content lives in its own file, it is easy to read and edit, and you do not need any registration code. Besides the fields already shown, the header block also accepts Post Types (restrict where the pattern appears in the inserter) and Block Types (associate the pattern with a specific block context, so it surfaces as a suggestion when that block is inserted).
Creating Pattern Categories
Custom categories help organize your patterns in the inserter. Register them using register_block_pattern_category():
add_action( 'init', function() {
register_block_pattern_category(
'mytheme-heroes',
array(
'label' => __( 'Hero Sections', 'mytheme' ),
)
);
register_block_pattern_category(
'mytheme-testimonials',
array(
'label' => __( 'Testimonials', 'mytheme' ),
)
);
register_block_pattern_category(
'mytheme-pricing',
array(
'label' => __( 'Pricing Tables', 'mytheme' ),
)
);
});
Then assign patterns to these categories using the categories property in either the PHP registration or the file header comment.
How to Get Block Markup for Patterns
The hardest part of creating patterns is getting the correct block markup. Here is the most reliable workflow:
- Design the layout visually in the WordPress block editor. Add blocks, adjust settings, apply colors, set spacing, everything you want in the pattern
- Switch to the Code Editor by clicking the three-dot menu in the top-right corner of the editor and selecting “Code editor”
- Copy the entire block markup, this is what goes into the
contentproperty of your pattern - Paste it into your pattern file (either the PHP registration or the
patterns/file) - Replace specific content with placeholders, change “Our Company” to “Your Heading Here” so editors know what to customize
This visual-first approach ensures your block markup is always valid. Writing block comments by hand is error-prone and unnecessary when the editor generates correct markup for you.
Synced Patterns: Shared Content Across Your Site
Synced patterns (formerly called reusable blocks) are different from regular patterns. When you edit a synced pattern, the changes appear everywhere that pattern is used. This is ideal for content that needs to stay consistent across your site:
- Call-to-action banners that appear on multiple pages
- Announcement bars for promotions or events
- Standard disclaimers or legal notices
- Team bios or company info blocks used in footers and about pages
Creating Synced Patterns
In the editor, select one or more blocks, click the three-dot menu, and choose “Create pattern”. Toggle the “Synced” option to create a synced pattern. Give it a descriptive name and assign a category.
Synced patterns are stored as wp_block posts in the database. You can manage them from Appearance > Editor > Patterns in the WordPress admin. From there you can edit, duplicate, or delete synced patterns.
Pattern Locking: Protecting Structure Without Freezing Content
A regular unsynced pattern gives editors full freedom to change anything after insertion, which is sometimes too much freedom. Block locking lets you restrict specific actions on specific blocks within a pattern: preventing a block from being moved, preventing it from being removed, or both, while still letting the editor change the text or image inside it.
To lock a block from the editor, select it, open the three-dot options menu, and choose “Lock.” You can lock movement, removal, or both. When you build a pattern this way and then save it (or export its markup into a file-based pattern), the lock attributes travel with the block markup, so every insertion of that pattern respects the same restrictions.
This matters most for patterns that have a structural requirement, a two-column layout where removing one column breaks the design, or a footer pattern where a legal disclaimer block must never be deleted by an editor working quickly. Lock the structural blocks, leave the content blocks (headings, paragraphs, images) unlocked, and you get a pattern that is simultaneously safe from structural damage and fully editable where it should be.
Advanced: Dynamic Patterns with PHP
Because file-based patterns are PHP files, you can use PHP logic to create dynamic patterns. This is powerful for patterns that need to pull data from WordPress:
<?php
/**
* Title: Latest Posts Grid
* Slug: mytheme/latest-posts-grid
* Categories: posts
*/
$recent_posts = get_posts( array(
'numberposts' => 3,
'post_status' => 'publish',
) );
if ( empty( $recent_posts ) ) {
return;
}
?>
<!-- wp:group {"align":"wide","layout":{"type":"constrained"}} -->
<div class="wp-block-group alignwide">
<!-- wp:heading -->
<h2 class="wp-block-heading">Latest from Our Blog</h2>
<!-- /wp:heading -->
<!-- wp:columns -->
<div class="wp-block-columns">
<?php foreach ( $recent_posts as $post ) : ?>
<!-- wp:column -->
<div class="wp-block-column">
<!-- wp:heading {"level":3} -->
<h3 class="wp-block-heading"><?php echo esc_html( $post->post_title ); ?></h3>
<!-- /wp:heading -->
<!-- wp:paragraph -->
<p><?php echo esc_html( wp_trim_words( $post->post_content, 20 ) ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:column -->
<?php endforeach; ?>
</div>
<!-- /wp:columns -->
</div>
<!-- /wp:group -->
Use dynamic patterns carefully. The PHP executes when the pattern is inserted, not on every page load. Once the blocks are in the content, they become static. For truly dynamic content, use the Query Loop block or custom dynamic blocks instead.
Pulling in Custom Field Data
The same technique extends to custom fields. If your site uses Advanced Custom Fields or the core custom fields API, a dynamic pattern can pre-populate a layout with real field values instead of placeholder text, which is useful for patterns tied to a specific custom post type, a staff directory entry or a product spec sheet, for example. Fetch the field value with get_field() or get_post_meta() inside the pattern file, escape it the same way you would escape any other output, and interpolate it into the block markup before the pattern is inserted. Because the PHP only runs at insertion time, editors still end up with plain, editable block content afterward, not a permanent dependency on the custom field staying populated.
Using theme.json to Style Your Patterns
Your patterns inherit styles from theme.json. This means colors, typography, spacing, and layout settings defined in your theme’s global styles automatically apply to pattern blocks. To make your patterns look consistent with the rest of your theme:
- Use preset colors (like
has-primary-color) instead of hardcoded hex values - Use preset font sizes (like
has-large-font-size) instead of pixel values - Use spacing presets from your theme’s spacing scale instead of fixed padding and margin values
This approach ensures that when someone changes the primary color in the Site Editor’s global styles, your patterns update automatically. Hardcoded values create patterns that look out of place when the theme styles change.
Common Errors and How to Fix Them
A handful of mistakes account for most of the “my pattern isn’t showing up” or “my pattern looks broken” reports.
- Pattern doesn’t appear in the inserter. Almost always a slug collision or a missing
Slugheader in a file-based pattern. Confirm the slug is unique across your active theme and any active plugins, and that it follows thenamespace/nameformat exactly. - Pattern inserts but renders unstyled or broken. This usually means the block markup was hand-edited and a block comment got out of sync with its HTML, or an attribute like a unique class name (
wp-container-core-buttons-is-layout-*) was copied from one insertion and now collides elsewhere. Always regenerate markup from the Code Editor rather than hand-patching it. - Dynamic PHP pattern throws a fatal error. Usually a missing null check before looping over query results, exactly like the empty-check in the Latest Posts Grid example above. Any dynamic pattern that queries the database needs to handle the zero-results case gracefully, since an empty query is a normal outcome, not an exception.
- Synced pattern changes don’t appear where expected. Confirm the block was actually inserted as a synced reference and not converted to a regular pattern at some point, editors can accidentally detach a synced instance from the three-dot menu, after which it stops receiving updates.
Accessibility and Testing Before Shipping a Pattern
A pattern gets reused dozens or hundreds of times across a site, so an accessibility gap in the pattern gets multiplied by every insertion. Before adding a pattern to your library, check a short list:
- Heading levels inside the pattern make sense relative to where it’s likely to be inserted; a hero pattern that hardcodes an
h1will conflict with the page title on most templates, so anh2is usually the safer default. - Any image included as placeholder content has meaningful alt text guidance in the pattern description, so editors know to replace it rather than leave a generic filename.
- Color combinations baked into the pattern (background and text pairs, button states) meet contrast requirements at the values your theme.json defines, not just at whatever preview color happened to look fine during design.
- Interactive elements, buttons, accordion toggles, tabs, remain keyboard reachable after the pattern is inserted and edited, not just in the original design file.
Pair this with the practical best practice already common in mature pattern libraries: test every pattern at mobile, tablet, and desktop widths before publishing it, since a pattern that looks great in the 1200px preview can collapse awkwardly at 375px if the underlying blocks weren’t given sensible responsive behavior.
Pattern Best Practices
- Use descriptive names and keywords so editors can find patterns quickly in the inserter
- Set appropriate
viewportWidthfor each pattern so the preview in the inserter accurately represents the layout - Include placeholder content that makes it obvious what editors should replace (“Your Heading Here” instead of “Lorem Ipsum”)
- Group related blocks inside a Group or Cover block so the entire pattern can be moved or deleted as one unit
- Test patterns at multiple screen sizes, a pattern that looks great on desktop but breaks on mobile is not ready for production
- Use the
patterns/directory for file-based patterns whenever possible, it is cleaner thanregister_block_pattern()calls in PHP - Prefer theme.json presets over hardcoded values for colors, fonts, and spacing so patterns adapt to style changes
- Lock structural blocks, leave content unlocked for any pattern where a missing column or removed disclaimer would break the layout or a compliance requirement
For deeper control over how patterns look, our guide on configuring global styles in theme.json covers the styles section, element-level styling, and CSS custom properties that your patterns inherit.
FAQ
Can I use the same pattern across multiple themes?
File-based patterns registered by a theme are only available while that theme is active. For patterns you want available across theme switches, register them from a small companion plugin instead of the theme itself, or use synced patterns, which live in the database as wp_block posts and persist independently of the active theme.
Why does my pattern show unwanted extra spacing when inserted?
This is almost always inherited block gap or padding settings from a parent Group or Cover block in the pattern markup, combined with your theme.json’s global block gap value stacking on top of it. Check the pattern’s outer wrapper block for an explicit style attribute setting its own gap or padding, and remove it if you want the pattern to simply inherit the surrounding context.
Should I build a pattern library plugin instead of putting patterns in the theme?
If the patterns are genuinely tied to this theme’s design language, keep them in the theme. If you maintain multiple sites or themes and want a consistent pattern library across all of them, a small dedicated plugin that registers the shared patterns is the more maintainable choice, since it decouples the patterns from any single theme’s lifecycle.
Can editors create their own patterns without touching code?
Yes. Any user with permission to edit content can select blocks in the editor and choose “Create pattern” from the three-dot menu, which saves it as a synced or unsynced wp_block post manageable from Appearance > Editor > Patterns. This is the right path for content-driven, ad hoc reuse; PHP-registered patterns are the right path for patterns that are part of the theme’s actual design system and need to ship with the theme itself.
Do patterns slow down the editor if I register a lot of them?
A theme with a few dozen well-organized patterns has no noticeable performance impact in the editor. The practical limit shows up earlier than performance does: once a pattern library grows past roughly 40 to 50 entries without solid categories and keywords, editors simply stop being able to find what they need and start rebuilding layouts from scratch instead. Invest in category structure and searchable keywords before worrying about raw pattern count.