WordPress Developer Blog

How to modify theme.json data using server-side filters

In recent years, theme.json has transformed how themes define styles and settings in WordPress, bringing centralized control to design and Editor functionality. And theme.json continues to receive new features with every release.

However, configuring theme.json data solely through a theme’s theme.json file can be challenging, especially if you need dynamic functionality or advanced Editor customization that is not supported. To overcome these limitations, the WordPress project has introduced powerful tools like server-side theme.json filters, offering greater flexibility.

Throughout this guide, you’ll: 

  • Explore the intricacies of theme.json data layers
  • See practical examples of those filters in use
  • Learn to curate Editor functionality to suit your needs

From dynamically modifying color palettes to restricting block controls based on user permissions, you’ll discover a world of options that these server-side filters can offer.

Understanding theme.json data layers

When using a WordPress theme that includes a theme.json file, there are four layers (origins) of theme.json data that contribute to the overall configuration:

  • default – Data that includes default settings and styles that come with WordPress.
  • block – Data that comes from blocks.
  • theme – Data that comes from the theme.
  • custom  – Data that is user-generated from Global Styles. Whenever you change a color, font, block style, etc., in the Site Editor, these are stored as custom (user) data.

All these layers combine to create the final theme.json data object, which controls how the Editor works and the ultimate look and feel of your website.

You can find a detailed overview of general theme.json concepts in the Block Editor Handbook. To see the source code that controls each data layer, refer to the class-wp-theme-json-resolver.php file in WordPress Core.

WordPress 6.1 introduced four server-side filters that let you modify these different layers of theme.json data:

  • wp_theme_json_data_default – Hooks into default data provided by WordPress.
  • wp_theme_json_data_blocks – Hooks into data provided by blocks.
  • wp_theme_json_data_theme – Hooks into data provided by the theme.
  • wp_theme_json_data_user – Hooks into data provided by the user (custom).

These layers have their own hierarchy: the custom (user) layer is the highest priority. The lowest is the default layer.

For instance, you can define default theme.json settings and styles with the wp_theme_json_data_default filter and still let blocks, themes, or users override this data. On the other hand, if you want to override all other potential data sources completely, you can apply specific theme.json settings with the wp_theme_json_data_user filter. 

Now, let’s dive into some practical examples.

Getting started with theme.json filters

Filtering theme.json data works pretty much like any other PHP hook. 

You register a callback with the add_filter() function. That callback function will receive an instance of the WP_Theme_JSON_Data class, which contains the data for the layer you’re filtering. Then you can modify that data and return an updated theme.json.

Here’s a simple example, slightly modified from the original Dev Note, that changes the color palette of the current theme and disables the text color control in the Editor:

<?php
/**
 * This function modifies the theme JSON data by updating the theme's color
 * palette to just black or white and disables text color.
 *
 * @param object $theme_json The original theme JSON data.
 * @return object The modified theme JSON data.
 */
function example_filter_theme_json_theme( $theme_json ){
	$new_data = array(
		'version'  => 2,
		'settings' => array(
			'color' => array(
				'text'       => false,
				'palette'    => array(
					array(
						'slug'  => 'base',
						'color' => 'white',
						'name'  => __( 'Base', 'theme-domain' ),
					),
					array(
						'slug'  => 'contrast',
						'color' => 'black',
						'name'  => __( 'Contrast', 'theme-domain' ),
					),
				),
			),
		),
	);

	return $theme_json->update_with( $new_data );
}
add_filter( 'wp_theme_json_data_theme', 'example_filter_theme_json_theme' );

This is how the applied filter will appear for users in the Editor.

Keep in mind a few important things as you work with server-side filters:

  1. To update the original theme.json data with your modifications, use the update_with( $new_data ) method and make sure your custom data follows a valid theme.json-like structure.
  2. New data also requires a version number. If your data uses a different version than the data from the filtered layer, WordPress will properly migrate it at runtime—even if the structure of theme.json changes in the future. 

Now, the code provided above is designed to be placed inside your theme’s functions.php file. 

If you want to use a utility plugin, you need to wait for the theme to load before the filter can run. In that case, you’ll want to use the after_setup_theme hook. It’s also a good practice to check if the theme has a theme.json file using the wp_theme_has_theme_json() function. 

The modified code would look something like this:

// For the filter to work properly, it must be run after theme setup.
function example_apply_theme_json_theme_filters() {

	// Check to make sure the theme has a theme.json file.
	if ( wp_theme_has_theme_json() ) {
		add_filter( 'wp_theme_json_data_theme', 'example_filter_theme_json_theme' );
	}
}
add_action( 'after_setup_theme', 'example_apply_theme_json_theme_filters' );

Now that you know the basics, let’s explore some more interesting examples.

Modifying theme.json based on login status

Let’s say you want to change the color of a theme depending on whether a user is logged in. Perhaps this could be a visual cue to let customers know they are receiving a loyalty discount.

One of the benefits of server-side filtering is that it works well with standard WordPress PHP functions, like is_user_logged_in(). This function allows you to quickly determine the login status of a user. You can then use this information to change theme.json settings when combined with the wp_theme_json_data_theme filter. 

The following example shows how to change the color palette and a few button styles in the Twenty Twenty-Three theme. This code is specifically designed for the color naming conventions used in Twenty Twenty-Three, so you will need to adapt it accordingly for other themes.

<?php
/**
 * This function modifies the theme JSON data by updating the theme's color
 * palette based on the user's login status.
 *
 * @param object $theme_json The original theme JSON data.
 * @return object The modified theme JSON data.
 */
function example_modify_color_palette_if_logged_in( $theme_json ) {
	if ( is_user_logged_in() ) {
		$new_data = array(
			'version'  => 2,
			'settings' => array(
				'color' => array(
					'palette' => array(
						array(
							'color' => '#ffffff',
							'name'  => __( 'Base' ),
							'slug'  => 'base',
						),
						array(
							'color' => '#00131C',
							'name'  => __( 'Contrast' ),
							'slug'  => 'contrast',
						),
						array(
							'color' => '#3858E9',
							'name'  => __( 'Primary' ),
							'slug'  => 'primary',
						),
						array(
							'color' => '#1D35B4',
							'name'  => __( 'Secondary' ),
							'slug'  => 'secondary',
						),
						array(
							'color' => '#ECF6FA',
							'name'  => __( 'Tertiary' ),
							'slug'  => 'tertiary',
						),
					),
				),
			),
			'styles'  => array(
				'elements' => array(
					'button' => array(
						'color' => array(
							'background' => 'var(--wp--preset--color--primary)',
							'text'       => 'var(--wp--preset--color--base)',
						),
					),
				),
			),
		);

		// Return the modified theme JSON data.
		return $theme_json->update_with( $new_data );
	}

	// Return the original theme JSON data.
	return $theme_json;
}

// For the filter to work properly, it must be run after theme setup.
function example_apply_theme_json_theme_filters() {

	// Check to make sure the theme has a theme.json file.
	if ( wp_theme_has_theme_json() ) {
		add_filter( 'wp_theme_json_data_theme', 'example_modify_color_palette_if_logged_in' );
	}
}
add_action( 'after_setup_theme', 'example_apply_theme_json_theme_filters' );

Notice that this code modified both styles and settings. In the Twenty Twenty-Three theme, buttons have dark text on a light background. The filter changes the color palette making the primary color blue, so the button text needs to be changed to base.

Here’s what this applied filter will look like for users on the front end.

While this example may not be relevant to your specific use case, it demonstrates the flexibility of PHP filters. One of the most important differences between client-side and server-side filters is that server-side filters update the theme.json data at the server level. This data is then processed and used to create CSS variables.

You can inspect the page’s HTML and CSS to see the explicit changes. The CSS variables for the color palette have been updated, which are then used to style the page.

This functionality has many implications. For example, you could add or modify custom theme.json settings. These settings generate custom CSS variables that you could use to create sophisticated design systems or provide specific functionality to a theme or block.

Here’s a callback function you could use to add custom settings that output CSS variables for viewport, media, or other breakpoints. The variables will take the form --wp--custom--breakpoint--[size], which can be used to adjust the look of your theme based on each breakpoint.

/**
 * This function modifies the theme JSON data by creating a custom
 * CSS variables for various breakpoints.
 *
 * @param object $theme_json The original theme JSON data.
 * @return object The modified theme JSON data.
 */
function example_create_custom_breakpoint_variables( $theme_json ) {

	$new_data = array(
		'version'  => 2,
		'settings' => array(
			'custom' => array(
				'breakpoint' => array(
					'small'  => '360px',
					'medium' => '780px',
					'large'  => '1080px',
				),
			),
		),
	);

	// Return the modified theme JSON data.
	return $theme_json->update_with( $new_data );
}

Next up, let’s look at ways you can use server-side filters to restrict user functionality in the Editor.

Restricting theme.json settings by user permissions 

By default, the theme.json file lets you control how the Editor works for everyone on your site. But what if you want to give users different permissions? Maybe you want to disable color controls for content creators but let Administrators modify colors sitewide. You can’t do that with the theme.json file, but you can use server-side or client-side filters.

Both client-side and server-side filters have their pros and cons. Client-side filters provide more flexibility to adjust settings directly within the Editor. Server-side filters make changes to theme.json data on the server, letting you configure settings and styles and even create CSS variables, as explained in the previous section.

Client-side filters are beyond the scope of this article but refer to Curating the Editor experience with client-side filters if you would like to learn more.

Now, imagine you manage multiple websites using different themes with unknown theme.json file configurations.

Your goal is to maintain a consistent Editor experience across all sites using a utility plugin. To keep things simple, let’s say you want to turn off color controls for everyone but Administrators (who should have access to everything). Since you don’t know what themes have enabled or disabled certain features sitewide, you also need to ensure there are no restrictions for admins.

You could use the same filter as the examples above, but let’s use wp_theme_json_data_user, guaranteeing that the custom settings always take priority over any theme configurations.

<?php
/**
 * This function modifies the theme JSON data by disabling color settings
 * for all users and then specifically enabling settings for users with the 
 * capability to edit_theme_options (Administrators).
 *
 * @param object $theme_json The original theme JSON data.
 * @return object The modified theme JSON data.
 */
function example_restrict_color_settings_to_administrators( $theme_json ) {

	// First disable color settings for everyone. This will override
	// any settings that might have been supplied by the theme.
	$default_settings = array(
		'version'  => 2,
		'settings' => array(
			'color' => array(
				'text'       => false,
				'background' => false,
				'link'       => false,
			),
		),
	);

	$theme_json->update_with( $default_settings );

	// If the current user has the correct permissions, enable color settings.
	if ( current_user_can( 'edit_theme_options' ) ) {
		$administrator_settings = array(
			'version'  => 2,
			'settings' => array(
				'color' => array(
					'text'       => true,
					'background' => true,
					'link'       => true,
				),
			),
		);

		$theme_json->update_with( $administrator_settings );
	}

	// Return the modified theme JSON data.
	return $theme_json;
}

// For the filter to work properly, it must be run after theme setup.
function example_apply_theme_json_user_filters() {

	// Check to make sure the theme has a theme.json file.
	if ( wp_theme_has_theme_json() ) {
		add_filter( 'wp_theme_json_data_user', 'example_restrict_color_settings_to_administrators' );
	}
}
add_action( 'after_setup_theme', 'example_apply_theme_json_user_filters' );

By applying this filter, only users who have permission to edit_theme_options can view the color controls in the Editor. Unless you have made customizations to your WordPress installation, this will typically include only Administrators. For further information on checking roles and capabilities, you can refer to the Plugin Handbook

Here’s how the outcome will appear for both Editors and Administrators.

Now that you know how to modify theme.json data using these four server-side filters, you can start thinking about other applications of this functionality.

Say you’re using Twenty-Three but want to avoid creating and managing a child theme for a few minor changes. You could write a small utility plugin that overrides theme.json’s settings and styles using the wp_theme_json_data_theme filter.

Or you could use the wp_theme_json_data_block filter to apply styles to your custom blocks. This would provide a better default experience while allowing themes and users to override this configuration.

The possibilities are nearly endless.

For more information and a working plugin that explores the examples in this article, you can check out the Editor Curation Examples plugin on GitHub. The Block Editor Handbook also includes a fantastic resource of additional curation methods, and every WordPress release adds more.

So what will you build using the server-side theme.json filters? Is there functionality that’s missing that you would like to see? Add your thoughts in the comments.

Props to @marybaum, @greenshady, @bph, @juanmaguitar, and @mburridge for feedback and review.

Leave a Reply