Sync Patterns and Template Parts Between Sites in Multisite

In this tutorial, you will learn how you can reuse WordPress synced patterns and template parts across multiple sites in a WordPress multisite network.

Even if you’re not using a WordPress Multisite installation at the moment, this tutorial may be helpful for you because it will allow you to understand how patterns, templates, and template parts work under the hood and how they are stored in the database.

This tutorial is a way of sharing my experience of working with patterns, template parts, and templates in my Simple Multisite Crossposting plugin. So, if you don’t want to dive deep into the whole coding thing, you can check out my plugin instead.

What is the Difference Between Patterns, Template Parts, and Block Templates?

I think it is a good idea to clarify this thing before we start syncing all the stuff across our multisite network. Because it is pretty easy to get lost in all these names: “What is a reusable block?”, “What is the difference between reusable blocks and synced patterns?”, and so on.

To simplify things, let’s take a look first at what we had in Gutenberg before the Site Editor and block themes appeared:

NameDescription
PatternsA bunch of blocks combined, you can insert them into any post together.
Synced patterns (former reusable blocks)It is also a bunch of combined blocks, however when you insert them into a post and then try to make any changes – the changes are going to be reflected in all the posts they are inserted.

It is also a custom post type wp_block.

Available only in Block Themes:

NameDescription
Template partsIt is kind of the same thing as a synced pattern, however, you can only use them inside the Site Editor (in block themes).

Custom post type: wp_template_part.
TemplatesTemplates are applied to a whole website page and can contain template parts.

Custom post type: wp_template.

Syncing Between Sites in a Multisite Network

First of all, let’s figure out what exactly we’re trying to achieve here.

  • Do you want to sync posts and your goal is to prevent patterns and template parts from being broken after syncing posts?
  • Or do you want to automatically sync patterns and template parts to every site of your multisite network, so later you can use them in posts?

In this tutorial, we’re going to discuss mostly the second way, if you’re interested in the first one, then I’d recommend you to check out my plugin instead.

Patterns

By the way, on my blog, I already have an article about creating patterns programmatically, and right now we’re going to apply the same principles for the multisite setup.

Below, I’m using the publish_wp_block hook which is fired every time a pattern is created or updated.

add_action( 'publish_wp_block', 'rudr_sync_reusable_blocks', 25, 2 );
function rudr_sync_reusable_blocks( $post_id, $post ) {

	// for unsynced patterns
	$sync_status = get_post_meta( $post_id, 'wp_pattern_sync_status', true );

	$site_ids = get_sites(
		array(
			'fields' => 'ids',
			'site__not_in' => get_current_blog_id(),
			// also good to mention
			'archived' => 0,
			'deleted' => 0,
			'spam' => 0,
		)
	);
	// or you can just hardcode some specific site IDs like this
	// $site_ids = array( 22 );

	remove_action( 'publish_wp_block', __FUNCTION__, 25, 2 );

	foreach( $site_ids as $site_id ) {
		switch_to_blog( $site_id );

		$crossposted_block = get_page_by_title( $post->post_title, OBJECT, 'wp_block' );
		if( $crossposted_block ) {
			$crossposted_block_id = wp_update_post(
				array(
					'ID' => $crossposted_block->ID,
					'post_content' => $post->post_content,
				)
			);
		} else {
			$crossposted_block_id = wp_insert_post(
				array(
					'post_type' => 'wp_block',
					'post_content' => $post->post_content,
					'post_title' => $post->post_title,
					'comment_status' => 'closed',
					'ping_status' => 'closed',
					'post_status' => 'publish',
				)
			);
		}
		if( ! is_wp_error( $crossposted_block_id ) ) {
			if( 'unsynced' === $sync_status ) {
				update_post_meta( $crossposted_block_id, 'wp_pattern_sync_status', 'unsynced' );
			}
		}
		restore_current_blog();
	}

	add_action( 'publish_wp_block', __FUNCTION__, 25, 2 );

}

Let’s pay attention to some details from the code above:

  • I am using the standard WordPress function get_sites() to get all the subsites in a multisite network we’re going to sync our pattern. Don’t forget to exclude the current site from the return array using the site__not_in parameter.
  • As I already mentioned before, patterns are just a custom post type wp_block, that’s why it is enough to use wp_insert_post() and wp_update_post() functions. It is also the reason why we can easily use the get_page_by_title() function to check whether the same pattern is already created on a subsite and only update it in this case.
  • I also hardcoded the pattern sync status, because it only accepts one value at this moment, which is unsynced, and by default, all created patterns are synced ones.

What about block pattern categories? If you want to sync them too, you can just add to the code the following lines:

$block_categories = wp_get_object_terms( $post_id, 'wp_pattern_category', array( 'fields' => 'slugs' ) );

Please keep in mind, that in order to simplify things, I am only applying existing pattern categories. So this code won’t create new pattern categories correctly on a subsite.

if( $block_categories ) {
	wp_set_object_terms( $crossposted_block_id, $block_categories, 'wp_pattern_category', false );
}

Template parts

Syncing template parts (and block templates) is pretty much similar to syncing patterns, the only differences are:

  • you need to use publish_wp_template_part hook instead of publish_wp_block,
  • you can not check whether a template part already exists on a target subsite using its name or slug, you need to use the get_block_template() function instead.
  • ah, and yes, if you wish to sync template part categories as well, the custom taxonomy name is wp_template_part_area (not wp_pattern_category).

That’s pretty much it, let’s jump straight to the code now:

add_action( 'publish_wp_template_part', 'rudr_sync_template_part', 25, 2  );
function rudr_sync_template_part( $post_id, $post ) {

	// current theme before switching the current blog
	$theme_slug = get_template();

	// we can use get_sites() function as well
	$site_ids = array( 22, 23, 25 );

	remove_action( 'publish_wp_template_part', __FUNCTION__, 25, 2 );

	foreach ( $site_ids as $site_id ) {
		switch_to_blog( $site_id );

		$synced_template_part = get_block_template( "{$theme_slug}//{$post->post_name}", 'wp_template_part' );

		if( $synced_template_part ) {
			$synced_template_part_id = wp_update_post(
				array(
					'ID' => $synced_template_part->id,
					'post_content' => $post->post_content,
				)
			);
		} else {
			$synced_template_part_id = wp_insert_post(
				array(
					'post_type' => 'wp_template_part',
					'post_content' => $post->post_content,
					'post_title' => $post->post_title,
					'comment_status' => 'closed',
					'ping_status' => 'closed',
					'post_status' => 'publish',
				)
			);
		}

		wp_set_object_terms( $synced_template_part_id, $theme_slug, 'wp_theme' );

		restore_current_blog();
	}

	add_action( 'publish_wp_template_part', __FUNCTION__, 25, 2 );

}

I also want you to pay special attention to an interesting taxonomy – wp_theme. If you decide to skip this part, your template parts won’t appear on subsites, because every template part is connected to a specific block theme., they can not exist globally, for all themes at once.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 15 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X