Synced theme patterns

A theme pattern is usually two things at once: a design, and the words that happen to be sitting in it. A synced theme pattern separates them. The design is rendered from the file wherever the pattern is used, and each use says what goes in the parts the pattern marked as fillable.

Making a theme pattern synced

One header does it. Add Synced: yes to the pattern file, or flip the Synced status panel in Pattern Builder, which writes the header for you:

<?php
/**
 * Title: Notice
 * Slug: my-theme/notice
 * Synced: yes
 */
?>

From then on, inserting the pattern stores a reference rather than a copy:

<!-- wp:pattern {"slug":"my-theme/notice"} /-->

The design is rendered from the theme file and cannot be edited on the page. Edit the file, in Pattern Builder or in your editor of choice, and every notice on the site changes. In the block editor, an instance’s toolbar carries an Edit Pattern button that opens the pattern itself. No post ID is involved anywhere: the reference is the slug, so it survives a database reset, a migration, and a move to another site that has the theme.

Content slots

A pattern that could not be changed at all would not be much use, so a synced pattern marks the parts that are meant to be filled in. This is WordPress core’s own Pattern Overrides syntax: give the block a name in its metadata, and bind it to the pattern-overrides source.

<!-- wp:heading {"metadata":{"name":"headline","bindings":{"__default":{"source":"core/pattern-overrides"}}}} -->
<h2 class="wp-block-heading">Headline goes here</h2>
<!-- /wp:heading -->

metadata.name names the slot. __default binds every attribute the block allows to be overridden at once: the content of a heading or paragraph, the URL and alt text of an image, the text and URL of a button. It is the form to reach for, and the only form WordPress lets someone type into inside a synced pattern instance in the editor. Name slots for what they hold (headline, question, cta) rather than for their block type.

The Pattern Bindings panel in Pattern Builder’s sidebar does this without touching JSON: select a block in a synced pattern and give it a slot name.

The placeholder copy in the file matters. It is what shows in the inserter preview, and it is what renders if a use of the pattern leaves that slot empty, so write realistic copy of the right length.

Filling the slots

In the editor, someone who inserts a synced pattern types into its slots directly, the way core’s synced patterns work. What the editor stores is a content attribute on the reference, keyed by slot name and then by attribute:

<!-- wp:pattern {"slug":"my-theme/hero","content":{
  "headline": { "content": "Built for the long haul" },
  "photo": { "url": "https://example.com/roof.jpg", "alt": "A tin roof" }
}} /-->

Any slot left out keeps the design pattern’s own content, so a pattern’s defaults double as its documentation. That attribute is not new: it is the same one WordPress already writes when you edit a field of a core synced pattern. What Pattern Builder adds is that a theme pattern can accept it too, and that it can be written in a file.

Filling them from another pattern

Because the reference is plain block markup, a pattern can fill another pattern’s slots. That is the design/content split: a synced design pattern owns the markup and carries placeholder copy, and a page pattern references it and supplies the words. A page built this way is a list of references and nothing else:

<!-- wp:pattern {"slug":"my-theme/faq-entry","content":{
  "question": { "content": "Is the plugin really free?" },
  "answer":   { "content": "Yes. Every local feature works without an account." }
}} /-->

<!-- wp:pattern {"slug":"my-theme/faq-entry","content":{
  "question": { "content": "Which WordPress version do I need?" },
  "answer":   { "content": "6.8 or later." }
}} /-->

A redesign then touches one file per component and no copy, and a copy change touches no markup. This website is built that way: every page is a page pattern filling the slots of a dozen design patterns, all in the theme’s files. In Pattern Builder the two halves are the Synced Design Pattern and Page Pattern kinds.

The runtime, and what to install in production

Everything above relies on two things WordPress core does not do: honouring Synced: yes on a theme pattern, and accepting a content attribute on the core/pattern block. Core’s pattern block declares no such attribute, so without a runtime WordPress drops it silently and renders the design pattern’s placeholder copy, and nothing reports a problem. A site that ships “A question somebody asks before buying” where the client’s question should be is what that failure looks like.

Two plugins provide the runtime, and they are the same code:

  • Pattern Builder ships it, so everything works while you build.
  • Synced Patterns for Themes ships it and nothing else: no admin screen, no editor tools, no REST routes. It is small, and it is the plugin a production site should depend on.

Pattern Builder is a development tool. Synced Patterns for Themes is for production. Install both while you work. When both are active Pattern Builder provides the runtime and the companion stays entirely unloaded, so there is no conflict and nothing to configure. Deactivate Pattern Builder on the live site and the companion takes over with identical rendering; both read the same Synced: yes header and the same content attribute. If your theme relies on synced theme patterns or on filling slots from files, say so in its documentation and install Synced Patterns for Themes on every site that runs it.

Pattern Builder also clears the companion’s cache whenever it writes a pattern file, so the hand-off never serves a stale pattern.

Checking your work

Slot mistakes are invisible to the editor’s block validation in both directions: a misspelled slot name fills nothing, and a slot whose attribute JSON lost a brace is still a valid block and is simply no longer a slot. Two habits catch them:

  • Look at the front end after filling a pattern. Placeholder copy where your words should be means a slot did not take.
  • Run the checks that ship with the plugin. The pattern-authoring guide in the plugin’s guides/pattern-author directory includes a markup validator and a slot checker (check-slots.php, run with WP-CLI) that renders a reference and reports which slots took their value and which still show the placeholder. Abilities and agents covers the validator.