Leveraging theme.json and per-block styles for more performant themes


With each major WordPress update, theme authors gain access to new tools that decrease their development workload and can increase the performance of their themes. Most of these improvements in the last few releases come from theme.json updates.

The theme.json file adds extra settings and styles that can replace the need for custom CSS. These styles are inlined and output on an as-needed basis. Major WordPress updates have continually brought more control to theme authors through the JSON format.

Another area where the block system shines is its feature for loading per-block styles. When WordPress detects a specific block on a particular front-end view, it will inline that block’s CSS inside of the <head> element of the site. It does this for third-party blocks as well as those from core.

All in all, it is a highly efficient system. And, when an entire site is made of blocks, such as with block themes, WordPress can often effectively inline only the bits of CSS needed for any given front-end view.

The potential downside of inlining all CSS is that you lose out on browser caching. However, considering that many WordPress themes’ stylesheets have ballooned in size over the last decade, the block system is, in all likelihood, more efficient than the average classic theme. But the system can only be as good as its weakest component. This puts much of the responsibility on the shoulders of theme authors to take advantage of the tools available.

To help encourage best practices, let’s talk about the two primary tools for ensuring a performant theme: theme.json and per-block styles.

Utilizing theme.json styles

Code editor that shows a theme.json file that is opened to the "styles" section.

Building WordPress themes in the modern era requires developers to rethink the approaches they have relied on in the past. It calls for them to go “all in” on a foundation that is somewhat outside of their control, relying on WordPress to do much of the heavy lifting.

The primary role of the theme is much the same as it has always been. It is the design, the paint that coats the walls of the house. However, the underlying architecture is all built atop a standard system.

This comes with some advantages, such as allowing all extensions—both plugins and themes—to benefit from the same performance-related features.

In the past, the onramp to theme design was style.css, but this is no longer the case. Much (not all) of what theme authors created via their stylesheets has been incorporated into theme.json. This can be a bit off-putting for those who are comfortable writing CSS. That is understandable. However, taking a step outside of a comfort zone can sometimes mean walking into a new and wonderful world of possibilities.

The usage of JSON is a part of a framework that allows WordPress, themes, and users to communicate, working together to build the website. WordPress provides the interface along with default settings and styles. Themes use theme.json to overrule the defaults. And users can make their own customizations through the interface, which is essentially custom JSON that’s stored in the database.

Because this all exists within this standard framework, it means that every performance improvement in new versions of WordPress percolates throughout the entire ecosystem.

These benefits require buy-in from theme authors. To get the most from modern WordPress, developers must use theme.json to configure as much of the site’s visual appearance as possible before moving onto stylesheet-based solutions. It is the foundation of block theme development, but classic theme authors can also take advantage of it.

The theme.json page in the Theme Handbook is the best place to start learning how to work with theme.json. And, the Living Reference in the Block Editor Handbook maintains an up-to-date reference of settings and styles. In particular, theme authors should always check if there is a style option available before reaching for custom CSS.

Each custom CSS rule placed in theme.json also benefits from the same contextual, inline-style system that blocks use.

Per-block styles

Code editor showing a core-group.css file with custom CSS for the Group block.

theme.json is not a magic bullet, a cure-all for every problem that a theme author is trying to solve. It is a tool that should be used to its fullest extent possible. Not all options are currently accessible via JSON, and there are times when it makes sense to use a stylesheet. However, the long-term aim is to use as little custom CSS as possible.

Traditionally, whenever a theme author wanted to add a bit of design flair (or, just any CSS, really), they would add code to their theme’s style.css file. In the early days of WordPress, it was not uncommon for themes to have non-minified, uncompressed stylesheets in the sub-10 kb range. The web was slower and websites simpler. As websites grew in complexity, stylesheets followed in size.

The following are the last three default themes from the classic era and their style.css file sizes:

  • Twenty Twenty-One: 158 kb
  • Twenty Twenty: 125 kb
  • Twenty Nineteen: 228 kb

Compared to many third-party themes, all three have relatively simple designs. The growing complexity of websites and their stylesheet sizes are problems that the block system seeks to solve.

While future default WordPress themes will likely rely primarily on theme.json for their design needs, developers in the outside world will often find themselves reaching for style.css to make customizations that are not yet possible via theme.json.

That is where wp_enqueue_block_style() comes in. It allows theme authors to take advantage of the same inline-style system that WordPress and block plugins get for free. This function allows theme authors to break up their stylesheets for individual blocks, which are only added as an inline style when they are needed.

Let’s take a look at a practical example. Suppose you had some custom CSS you needed to apply to the core Group block. Assuming your file was located in your theme’s assets/blocks/core-group.css file, use the following code in the theme’s functions.php file to enqueue it:

add_action( 'init', 'themeslug_enqueue_block_styles' );

function themeslug_enqueue_block_styles() {
    wp_enqueue_block_style( 'core/group', array(
        'handle' => 'themeslug-block-group',
        'src'    => get_theme_file_uri( "assets/blocks/core-group.css" ),
        'path'   => get_theme_file_path( "assets/blocks/core-group.css" )
    ) );
}

Unlike related wp_enqueue_* functions, you must provide the filepath to the CSS file along with the URL path. This is because WordPress needs to get the file’s content and print it inside the <head> element.

The above was an overly simplistic example. It is more than likely that theme authors will need to load multiple files, and rewriting that same code for each stylesheet would become a management nightmare.

When enqueuing styles for multiple blocks, use an array and loop through them instead. The following code snippet assumes that you have stylesheets for the core Button, Heading, and Paragraph blocks:

add_action( 'init', 'themeslug_enqueue_block_styles' );

function themeslug_enqueue_block_styles() {
    // Add the block name (with namespace) for each style.
    $blocks = array(
        'core/button'
        'core/heading',
        'core/paragraph'
    );

    // Loop through each block and enqueue its styles.
    foreach ( $blocks as $block ) {

        // Replace slash with hyphen for filename.
        $slug = str_replace( '/', '-', $block );

        wp_enqueue_block_style( $block, array(
            'handle' => "themeslug-block-{$slug}",
            'src'    => get_theme_file_uri( "assets/blocks/{$slug}.css" ),
            'path'   => get_theme_file_path( "assets/blocks/{$slug}.css" )
        ) );
    }
}

You could get even fancier with that and use PHP’s glob() function to automatically generate the array of files. However, the goal with the above code is for you to see the mechanics of it at work.

For those wondering about the core- prefix for the filenames and the need for the str_replace() call, there is a good reason for this. It’s set up that way because it also assumes that you may want to support third-party plugins with block names like pluginslug/blockslug and filenames like pluginslug-blockslug.css. If you only plan to support core blocks in your theme, you can always simplify the code.


For many block themes, there will not be a need for a style.css file at all, outside of adding the file header information with the theme name and other details. When every element on the page is a block, there is hardly a need for much else. There are certainly scenarios that fall outside of this scope, but the bulk of most theme CSS can be handled via theme.json and per-block styles.

Props to @juanmaguitar, @annezazu, @mburridge, @webcommsat, and @bph for feedback and review.

One response to “Leveraging theme.json and per-block styles for more performant themes”

  1. vishalgaikwad Avatar

    This article helped to clear an air of confusion/doubts on how to build block themes correctly. Such articles from the dev team are welcome, as many times as a theme builder I get lost into so many things said & written about the block themes. It becomes even more challenging considering its a transitioning phase. Thank you Justin for such a helpful post!

Leave a Reply

Your email address will not be published. Required fields are marked *