The Problem
A client moved to WordPress 7.0 last week and reported a strange editor bug a day later. Their content team uses a synced pattern for testimonial cards. The pattern has three blocks marked as overridable — the customer name, the quote, and the company. Editors drop the pattern on a page, fill in the three fields, hit Update, see the green "Page updated" notice, and the editor shows the new content. Refresh the page in the browser and every field snaps back to the default placeholder text. No PHP error, no console warning, no entry in the editor's revision history.
I ran into this on a client project mid-migration, so I know the failure mode well. The pattern itself is registered through a block theme using a PHP file in /patterns/testimonial.php. The pattern saves fine in 6.8. The exact same pattern, on the exact same content, breaks on 7.0. Custom block themes that load patterns through the file API are hit hardest, but I have seen sites loading patterns from the database through the pattern directory show the same behaviour intermittently.
The override values are visible in post_content if you query the database directly. The save is happening. Something in the render path is throwing them away.
Why It Happens
Pattern Overrides work by attaching a metadata.bindings object to each overridable block. The binding points to a source called core/pattern-overrides and a name that matches what you set in the synced pattern. On save, the editor serialises both the user content and the binding metadata into block comments. On render, WordPress walks the block tree, finds the binding, calls the source's get_value callback, and replaces the placeholder content with whatever the post stored.
WordPress 7.0 tightened the block bindings registry. The core/pattern-overrides source now requires its name to be registered through register_block_bindings_source() during the init action, with an explicit uses_context array that includes pattern/overrides. Block themes that ship patterns through /patterns/*.php get the registration for free because core does it during pattern loading. Themes that register patterns through register_block_pattern() in functions.php without the new templateTypes argument do not, and the source never lands in the registry by the time the render pass runs.
When the source is missing, the bindings resolver in 7.0 silently falls through to the placeholder content instead of throwing. That is the lie. The block tree still contains the override values, but the resolver cannot find a registered source to read them through, so it returns the pattern default. The post is fine. The render is broken. The official block bindings API docs cover the new shape but do not flag the silent-fallback behaviour as a breaking change.
The Fix
Two things need to happen. The pattern registration needs to declare itself bindings-aware, and the override source needs to be present in the registry before the first render pass.
Step 1: Register the bindings source explicitly in the theme. Add this to functions.php or a small mu-plugin. Even if core already does this in your version, registering twice is a no-op and protects you against the registration order changing on future patches:
add_action('init', function () {
if (! function_exists('register_block_bindings_source')) {
return;
}
if (\WP_Block_Bindings_Registry::get_instance()->is_registered('core/pattern-overrides')) {
return;
}
register_block_bindings_source('core/pattern-overrides', [
'label' => __('Pattern Overrides', 'qc-theme'),
'get_value_callback' => '__return_null',
'uses_context' => ['pattern/overrides'],
]);
}, 5);
The priority of 5 matters. Core registers patterns on the default priority of 10, and the pattern loader expects the source to be present when it walks the pattern markup. Registering at 5 puts you in front of the loader on every request, including the editor's REST calls.
Step 2: Update the pattern file header to declare the override slots. WordPress 7.0 introduced a Block Types header for pattern files that signals which blocks expose overrides. Without it, the editor still lets you mark blocks as overridable, but the saved post does not carry the binding context the resolver needs:
<?php
/**
* Title: Testimonial Card
* Slug: qc-theme/testimonial-card
* Categories: testimonials
* Block Types: core/post-content
* Sync Status: synced
*/
?>
<!-- wp:group {"metadata":{"name":"Testimonial Card"}} -->
<div class="wp-block-group">
<!-- wp:heading {"level":3,"metadata":{"name":"customer_name","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
<h3>Customer name</h3>
<!-- /wp:heading -->
<!-- wp:paragraph {"metadata":{"name":"quote","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
<p>Quote goes here.</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph {"metadata":{"name":"company","bindings":{"content":{"source":"core/pattern-overrides"}}}} -->
<p>Company</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
The Block Types: core/post-content header is what tells WordPress to wire the pattern up with the override context when it renders inside post content. Without that header, the pattern still appears in the inserter and still saves, but the bindings resolver gets a context object without the pattern/overrides key, hits the fallback, and you see your default text.
Step 3: Clear the pattern cache. WordPress 7.0 caches the registered patterns in a transient called wp_theme_files_patterns. Run this once after the changes:
wp transient delete wp_theme_files_patterns
wp cache flush
Reload the editor, place the pattern again, edit the fields, save, refresh. The content holds.
The Lesson
WordPress 7.0 did not break Pattern Overrides. It tightened the contract for how the bindings source is registered and made the fallback silent instead of loud. Themes that loaded patterns through register_block_pattern() without the new headers are hitting the new contract and losing the override path. Register the source at init priority 5, update the pattern file header, flush the transient, and the content sticks.
If your block theme is quietly losing override content and your editors are filing tickets you cannot reproduce, that is the kind of WordPress 7.0 migration audit I run for agencies. See my services. For a related WordPress 7.0 editor regression, read WordPress 7 block apiVersion iframe fix.
Pattern Overrides resetting on your theme? Let me fix it.
