Title: WP_Theme_JSON::merge
Published: July 20, 2021
Last modified: May 20, 2026

---

# WP_Theme_JSON::merge( WP_Theme_JSON $incoming )

## In this article

 * [Parameters](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#parameters)
 * [Source](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#source)
 * [Related](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#related)
 * [Changelog](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#changelog)

[ Back to top](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#wp--skip-link--target)

Merges new incoming data.

## 󠀁[Parameters](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#parameters)󠁿

 `$incoming`[WP_Theme_JSON](https://developer.wordpress.org/reference/classes/wp_theme_json/)
required

Data to merge.

## 󠀁[Source](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#source)󠁿

    ```php
    public function merge( $incoming ) {
    	$incoming_data    = $incoming->get_raw_data();
    	$this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data );

    	/*
    	 * Recompute all the spacing sizes based on the new hierarchy of data. In the constructor
    	 * spacingScale and spacingSizes are both keyed by origin and VALID_ORIGINS is ordered, so
    	 * we can allow partial spacingScale data to inherit missing data from earlier layers when
    	 * computing the spacing sizes.
    	 *
    	 * This happens before the presets are merged to ensure that default spacing sizes can be
    	 * removed from the theme origin if $prevent_override is true.
    	 */
    	$flattened_spacing_scale = array();
    	foreach ( static::VALID_ORIGINS as $origin ) {
    		$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );

    		// Apply the base spacing scale to the current layer.
    		$base_spacing_scale      = _wp_array_get( $this->theme_json, $scale_path, array() );
    		$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );

    		$spacing_scale = _wp_array_get( $incoming_data, $scale_path, null );
    		if ( ! isset( $spacing_scale ) ) {
    			continue;
    		}

    		// Allow partial scale settings by merging with lower layers.
    		$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $spacing_scale );

    		// Generate and merge the scales for this layer.
    		$sizes_path           = array( 'settings', 'spacing', 'spacingSizes', $origin );
    		$spacing_sizes        = _wp_array_get( $incoming_data, $sizes_path, array() );
    		$spacing_scale_sizes  = static::compute_spacing_sizes( $flattened_spacing_scale );
    		$merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes );

    		_wp_array_set( $incoming_data, $sizes_path, $merged_spacing_sizes );
    	}

    	/*
    	 * The array_replace_recursive algorithm merges at the leaf level,
    	 * but we don't want leaf arrays to be merged, so we overwrite it.
    	 *
    	 * For leaf values that are sequential arrays it will use the numeric indexes for replacement.
    	 * We rather replace the existing with the incoming value, if it exists.
    	 * This is the case of spacing.units.
    	 *
    	 * For leaf values that are associative arrays it will merge them as expected.
    	 * This is also not the behavior we want for the current associative arrays (presets).
    	 * We rather replace the existing with the incoming value, if it exists.
    	 * This happens, for example, when we merge data from theme.json upon existing
    	 * theme supports or when we merge anything coming from the same source twice.
    	 * This is the case of color.palette, color.gradients, color.duotone,
    	 * typography.fontSizes, or typography.fontFamilies.
    	 *
    	 * Additionally, for some preset types, we also want to make sure the
    	 * values they introduce don't conflict with default values. We do so
    	 * by checking the incoming slugs for theme presets and compare them
    	 * with the equivalent default presets: if a slug is present as a default
    	 * we remove it from the theme presets.
    	 */
    	$nodes        = static::get_setting_nodes( $incoming_data );
    	$slugs_global = static::get_default_slugs( $this->theme_json, array( 'settings' ) );
    	foreach ( $nodes as $node ) {
    		// Replace the spacing.units.
    		$path   = $node['path'];
    		$path[] = 'spacing';
    		$path[] = 'units';

    		$content = _wp_array_get( $incoming_data, $path, null );
    		if ( isset( $content ) ) {
    			_wp_array_set( $this->theme_json, $path, $content );
    		}

    		// Replace the presets.
    		foreach ( static::PRESETS_METADATA as $preset_metadata ) {
    			$prevent_override = $preset_metadata['prevent_override'];
    			if ( is_array( $prevent_override ) ) {
    				$global_path  = array_merge( array( 'settings' ), $prevent_override );
    				$global_value = _wp_array_get( $this->theme_json, $global_path, null );

    				$node_level_path  = array_merge( $node['path'], $prevent_override );
    				$prevent_override = _wp_array_get( $this->theme_json, $node_level_path, $global_value );
    			}

    			foreach ( static::VALID_ORIGINS as $origin ) {
    				$base_path = $node['path'];
    				foreach ( $preset_metadata['path'] as $leaf ) {
    					$base_path[] = $leaf;
    				}

    				$path   = $base_path;
    				$path[] = $origin;

    				$content = _wp_array_get( $incoming_data, $path, null );
    				if ( ! isset( $content ) ) {
    					continue;
    				}

    				// Set names for theme presets based on the slug if they are not set and can use default names.
    				if ( 'theme' === $origin && $preset_metadata['use_default_names'] ) {
    					foreach ( $content as $key => $item ) {
    						if ( ! isset( $item['name'] ) ) {
    							$name = static::get_name_from_defaults( $item['slug'], $base_path );
    							if ( null !== $name ) {
    								$content[ $key ]['name'] = $name;
    							}
    						}
    					}
    				}

    				// Filter out default slugs from theme presets when defaults should not be overridden.
    				if ( 'theme' === $origin && $prevent_override ) {
    					$slugs_node    = static::get_default_slugs( $this->theme_json, $node['path'] );
    					$preset_global = _wp_array_get( $slugs_global, $preset_metadata['path'], array() );
    					$preset_node   = _wp_array_get( $slugs_node, $preset_metadata['path'], array() );
    					$preset_slugs  = array_merge_recursive( $preset_global, $preset_node );

    					$content = static::filter_slugs( $content, $preset_slugs );
    				}

    				_wp_array_set( $this->theme_json, $path, $content );
    			}
    		}
    	}

    	/*
    	 * Style values are merged at the leaf level, however
    	 * some values provide exceptions, namely style values that are
    	 * objects and represent unique definitions for the style.
    	 */
    	$style_nodes = static::get_block_nodes(
    		$this->theme_json,
    		array(),
    		array( 'include_node_paths_only' => true )
    	);

    	// Add top-level styles.
    	$style_nodes[] = array( 'path' => array( 'styles' ) );

    	foreach ( $style_nodes as $style_node ) {
    		$path = $style_node['path'];
    		/*
    		 * Background image styles should be replaced, not merged,
    		 * as they themselves are specific object definitions for the style.
    		 */
    		$background_image_path = array_merge( $path, static::PROPERTIES_METADATA['background-image'] );
    		$content               = _wp_array_get( $incoming_data, $background_image_path, null );
    		if ( isset( $content ) ) {
    			_wp_array_set( $this->theme_json, $background_image_path, $content );
    		}
    	}
    }
    ```

[View all references](https://developer.wordpress.org/reference/files/wp-includes/class-wp-theme-json.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/7.0/src/wp-includes/class-wp-theme-json.php#L3349)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/7.0/src/wp-includes/class-wp-theme-json.php#L3349-L3500)

## 󠀁[Related](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#related)󠁿

| Uses | Description | 
| [_wp_array_set()](https://developer.wordpress.org/reference/functions/_wp_array_set/)`wp-includes/functions.php` |

Sets an array in depth based on a path of keys.

  | 
| [_wp_array_get()](https://developer.wordpress.org/reference/functions/_wp_array_get/)`wp-includes/functions.php` |

Accesses an array in depth based on a path of keys.

  |

## 󠀁[Changelog](https://developer.wordpress.org/reference/classes/wp_theme_json/merge/?output_format=md#changelog)󠁿

| Version | Description | 
| [6.7.0](https://developer.wordpress.org/reference/since/6.7.0/) | Replace background image objects during merge. | 
| [5.9.0](https://developer.wordpress.org/reference/since/5.9.0/) | Duotone preset also has origins. | 
| [5.8.0](https://developer.wordpress.org/reference/since/5.8.0/) | Introduced. |

## User Contributed Notes

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Fclasses%2Fwp_theme_json%2Fmerge%2F)
before being able to contribute a note or feedback.