Sync Taxonomy Terms Between Sites in a Multisite Network
In this guide, I will show you how to automatically create or update a taxonomy term (it could be a category, a tag, or a custom taxonomy) on one or multiple subsites within a WordPress multisite network when you create or update it on the main site.
More than that, we will also add a checklist with all subsites to the taxonomy edit page, so you can manually select specific subsites to which you want to sync this particular taxonomy term.
This is how it is going to look:

post_tag taxonomy).Below, we will create a mini-plugin, so you can just copy and paste the code from below to a new file in your wp-content/plugins folder and then just network-activate it.
<?php
/*
* Plugin name: Sync Taxonomy Terms in WordPress Multisite
* Description: The plugin displays a checklist of subsites in taxonomy edit pages and allows you to crosspost a term to select ones
* Version: 1.0
* Author: Misha Rudrastyh
* Author URI: https://rudrastyh.com
* Plugin URI: https://rudrastyh.com/wordpress-multisite/sync-taxonomy-terms.html
* Network: true
*/However, if your goal is only to sync taxonomy terms together with posts (for example, if “Post 1” has “Tag 1” and “Tag 2”, and you sync this post from “Site 1” to “Site 2”, you’d expect both tags to be automatically created on the target site as well, then probably you do not need to walk through this whole tutorial and it will be quite enough just to install and network activate the Simple Multisite Crossposting plugin and in the plugin settings, in the network admin dashboard Settings > Network Crosspost > Fields, activate taxonomy term syncing:

But you want to sync taxonomy terms independently, then this guide is for you.
Step 1. Displaying a List of Subsites When Editing a Term
First things first, we need to tap into {$taxonomy}_edit_form_fields (and optionally into {$taxonomy}_add_form_fields, if you want to display the subsites list on the page where you add new terms). More about adding custom fields to terms, you can read here.
And as you can see from the hook, we need to decide which taxonomy’s terms we want to sync between subsites in our multisite network. For example, let’s say I want to do it for WordPress tags taxonomy, whose slug is post_tag.
Then my hook is going to be:
add_action( 'post_tag_edit_form_fields', 'rudr_print_network_subsites', 10, 2 );Want to do it dynamically for all taxonomy terms, including custom taxonomies? Then we need to tap into this action hook within a loop, using the get_taxonomies() function and wrapping everything inside the admin_init hook.
add_action( 'admin_init', function() {
$taxonomies = get_taxonomies( array( 'show_ui' => true ), 'names' );
foreach( $taxonomies as $taxonomy ) {
add_action( "{$taxonomy}_edit_form_fields", 'rudr_print_network_subsites', 10, 2 );
}
} );Why use admin_init? Because if you run the get_taxonomies() function earlier, it will not return custom taxonomies.
Below is our final code for the checklist:
<?php
add_action( 'post_tag_edit_form_fields', 'rudr_print_network_subsites', 10, 2 );
function rudr_print_network_subsites( $term, $taxonomy ) {
// get a list of network subsites, getting only IDs will suffice
$blog_ids = get_sites( array(
'fields' => 'ids',
'public' => true,
) );
?><tr class="form-field">
<th>Sync to</th>
<td>
<ul>
<?php foreach( $blog_ids as $blog_id ) : ?>
<?php
$checked = get_term_meta( $term->term_id, "_crosspost_to_{$blog_id}", true );
?>
<li>
<label>
<input type="checkbox" name="subsite_<?php echo $blog_id ?>"<?php checked( $checked, true ) ?> />
<?php echo get_blog_option( $blog_id, 'blogname' ) ?>
</label>
</li>
<?php endforeach ?>
</ul>
<p class="description">Select subsites to which you need to sync this specific taxonomy term.</p>
</td>
</tr><?php
}Here is the result:

post_tag taxonomy).Of course, at this moment, the checkboxes will not even retain their state after you hit the “Update” button, so let’s continue to the second step.
Step 2. Syncing a Term to Selected Sites When Updating It
The code below requires the Rudr_Simple_Multisite_Crosspost class, which is part of Simple Multisite Crossposting, but you can use wp_create_term() if you want.
Why I decided to choose the Rudr_Simple_Multisite_Crosspost in my example?
When we sync a term to another subsite in a multisite network, there are a lot of things we need to keep in mind:
- How to handle parent terms and maintain the same hierarchy structure.
- Check if a term already exists and choose whether we should use a
wp_create_term()orwp_update_term()function. - What about custom fields? A taxonomy could have them.
So, to keep the example below as simple as possible, you can use prepare_term_data() and copy_term_data() methods and that’s pretty much it.
add_action( 'edited_post_tag', 'rudr_sync_term_to_subsites', 99, 3 );
function rudr_sync_term_to_subsites( $term_id, $tt_id, $args ) {
if( ! class_exists( 'Rudr_Simple_Multisite_Crosspost' ) ) {
wp_die(
sprintf(
'Please install and network activate <a href="%s">Simple Multisite Crossposting</a> plugin to continue.',
'https://rudrastyh.com/plugins/simple-multisite-crossposting',
)
);
}
$blog_ids = get_sites(
array(
'fields' => 'ids',
'public' => true,
)
);
if( empty( $blog_ids ) ) {
return;
}
if( empty( $_POST[ 'action' ] ) || 'editedtag' !== $_POST[ 'action' ] ) {
return;
}
remove_action( "edited_{$args[ 'taxonomy' ]}", __FUNCTION__, 99, 3 );
$crossposter_object = new Rudr_Simple_Multisite_Crosspost();
$term = get_term( $term_id );
$current_blog_id = get_current_blog_id();
$term_data = $crossposter_object->prepare_term_data( $term );
foreach( $blog_ids as $id ) {
if( isset( $_POST[ "subsite_{$id}" ] ) && $_POST[ "subsite_{$id}" ] ) {
update_term_meta( $term_id, "_crosspost_to_{$id}", true );
switch_to_blog( $id );
$crossposter_object->copy_term_data( $term_data, $args[ 'taxonomy' ] );
restore_current_blog();
} else {
update_term_meta( $term_id, "_crosspost_to_{$id}", false );
}
}
}What about the edited_post_tag hook? Can we use it for another taxonomy?
Sure, you can. More than that, you can combine it with the {$taxonomy}_edit_form_fields hook in the same loop, in the following way:
foreach( $taxonomies as $taxonomy ) {
add_action( "{$taxonomy}_edit_form_fields", 'rudr_print_network_subsites', 10, 2 );
add_action( "edited_{$taxonomy}", 'rudr_sync_term_to_subsites', 99, 3 );
}If you have any questions about the code in this tutorial or about the plugin, please feel free to ask them in the comments below.
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