Tips・WordPress multisite

Designing parent and child themes for multisite

Theme design for achieving "a shared look plus per-site differences" in multisite. Covers sharing a parent theme and expressing differences in child themes, versus branching inside a single theme.

In multisite, there’s one set of themes shared across the whole network. On top of that, how do you build “a shared look” and “per-site differences”? There are two design options.

  • Option A: a shared parent theme plus a child theme per site—when the differences are large
  • Option B: branching inside a single theme—when the differences are just colors and some wording

Choose based on how many sites there are and how big the differences get.

Distributing themes (network enabling)

wp-content/themes/
├── mynet-base/        ← the shared parent theme
├── mynet-shop/        ← a child theme (Template: mynet-base)
└── mynet-school/      ← a child theme (Template: mynet-base)
  • Network Admin → “Themes” → “Network Enable” puts it among every site’s options
  • To let only one site use it, go to “Sites” → edit that site → the “Themes” tab and enable it individually

The parent theme doesn’t need network enabling (activating a child theme uses the parent automatically). Distributing only the child themes prevents each site’s administrator from accidentally picking the parent.

TipsHow to set up WordPress multisiteHow to set up multisite itself

Option A: a parent theme plus per-site child themes

The shared skeleton lives in the parent, with each site’s look and differences in the child.

/*
Theme Name: MyNet Shop
Template: mynet-base
Version: 1.0.0
*/

:root {
  --color-primary: #e8618c;   /* this site's color only */
}
<?php
add_action('wp_enqueue_scripts', function () {
  wp_enqueue_style('parent-style', get_parent_theme_file_uri('style.css'), [], '1.0.0');
});

Templates with matching names are overridden by the child, and functions.php is loaded from both. That behavior is the same under multisite.

TipsHow to build a child theme (minimal setup)Child theme basics (the Template line, the override rules)

When it fits—layouts and template structures differ between sites, five sites or fewer, different people adjusting each design.

When it hurts—more than ten sites (ten child themes, and every shared fix means checking all of them).

Option B: branching inside a single theme

When the differences amount to colors, logos and wording, keeping one theme and branching inside it is easier to maintain.

<?php
/** Hold each site's settings in one place */
function mynet_site_config() {
  $path = trim(get_blog_details()->path, '/');   // e.g. 'shop'

  $config = [
    'shop'   => ['color' => '#e8618c', 'label' => 'Online shop'],
    'school' => ['color' => '#3d7ea6', 'label' => 'School'],
  ];

  return $config[$path] ?? ['color' => '#333333', 'label' => get_bloginfo('name')];
}

/** Output the color as a CSS variable */
add_action('wp_head', function () {
  $c = mynet_site_config();
  echo '<style>:root{--color-primary:' . esc_attr($c['color']) . ';}</style>';
});

The trick is to collect the settings into a single array. Adding a site is then one more line in that array.

TipsIdentifying the current child site by slug in multisiteHow to tell which site you’re on (slug, ID, domain)

Applying per-site CSS (adding a class to body)

A handy trick under either option.

<?php
add_filter('body_class', function ($classes) {
  if (is_multisite()) {
    $path = trim(get_blog_details()->path, '/');
    $classes[] = 'site-' . ($path !== '' ? sanitize_html_class($path) : 'main');
    $classes[] = 'site-id-' . get_current_blog_id();
  }
  return $classes;
});
.site-shop .site-header { background: var(--color-primary); }
.site-school .site-header { background: #3d7ea6; }
.site-main .hero { min-height: 70vh; }

You can express per-site differences inside a single CSS file, so you can adjust things without adding child themes.

TipsWordPress conditional tags cheat sheetHow filters like body_class work is covered in the hooks article

Swapping shared parts per site

Mixing the site slug into a template part’s name lets you give one site a different part.

<?php
$slug = trim(get_blog_details()->path, '/');
// use parts/header-shop.php if it exists, otherwise parts/header.php
get_template_part('parts/header', $slug ?: null);
?>

get_template_part('parts/header', 'shop') looks for parts/header-shop.php and falls back to parts/header.php when it’s missing. Using that mechanism, you swap parts without writing conditionals.

TipsTemplate hierarchy cheat sheet (which file gets used?)The order templates are looked for (the hierarchy)

Holding network-wide settings in one place

Values like “show the same company details in every site’s footer” can live in network-wide options.

<?php
// shared across the network (the same value from any site)
update_site_option('mynet_company_tel', '+1 555-0123');
echo esc_html(get_site_option('mynet_company_tel'));

// per site (an ordinary option)
update_option('mynet_catch', 'Welcome');
echo esc_html(get_option('mynet_catch'));
?>

get_site_option() is network-wide, get_option() is that site only. Choosing between those two matters a lot when building themes for multisite.

Which option should you pick?

Option A: separate child themesOption B: one theme with branching
Suits how many sitesUp to 55 or more
Size of the differencesLayouts differColors, logos, wording
Effort for a shared fixCheck every child themeOne place
Effort to add a siteBuild a child themeOne line in an array
Freedom per maintainerHighLow (but consistent)

Summary

  1. Themes are one shared set across the network. Distribute them by network enabling or enabling per site
  2. Big differences call for a parent plus child themes; colors and wording call for one theme plus a settings array
  3. Adding the site slug to body_class lets one CSS file cover per-site adjustments
  4. Shared values go in get_site_option(), site-specific values in get_option()

Deciding up front how much stays shared gives you a structure that holds up as sites are added.

FAQ

How are themes shared in multisite?
There's one set of themes for the whole network. Network-enabling a theme in the network admin makes it available to every site, and each site's administrator picks from the enabled themes. To restrict a theme to one site, enable it individually from that site's edit screen.
How do I give each site a different design?
The standard approach is a shared parent theme with a child theme per site overriding colors and components. If the only differences are colors or images, branching by site ID or slug inside a single theme is easier to manage.
Doesn't managing lots of child themes get painful?
It does. Past ten sites or so, leaning on "one theme plus configuration values" is more maintainable than mass-producing child themes. If the differences are CSS only, overriding CSS variables is enough.
Is there an easy way to apply per-site CSS?
Add the site's slug as a class via the body_class filter and you can make per-site adjustments inside a single CSS file, using selectors built on classes like site-shop.