Block.json Version 3: A Guide for Plugin and Theme Developers

Colorful building blocks
Blocks for everyone!

Block.json received a small upgrade with WordPress 6.3, which upped the version of the API to version 3 (v3).

So what does this entail, why should you care about it, and what can plugin and theme developers do with the new API version?

What is Block.json Version 3?

Version 3, as of this writing, enables an iframe to wrap around block content in the site and block editor. This separates the content area from admin styles, avoiding conflicts and style clashes. If version 3 is enabled, the block content will be neatly wrapped in an iframe, allowing theme and plugin authors a way to hook in and add their own customizations. This is particularly useful for theme developers who have long wished to be able to have better styling control in the block editor.

As of WordPress 6.3, the Post Editor is iframed if all registered blocks have a Block API version 3 or higher and no traditional metaboxes are registered.
Source: GitHub

The block.json file only comes into play for block developers. This is a version flag, so think of this as a “go” or “no go” panel of lights for all blocks. If all blocks have opted-in, version 3 should be auto-enabled assuming there’s no custom field trickery.

Panel of lights, ready set go.
Opting in to Version 3 is a Go, No-go Situation

How do you opt-in to Block.json and v3?

It’s an incredibly frustrating experience <sarcasm>. All you have to do is change your Block.json’s API version to 3. It looks like this:

Sample Block.json With the API Version of 3
Sample Block.json With API Version of 3

That’s right. Changing from version 2 to 3 is as simple as changing the version number for the apiVersion parameter.

What changes with version 3?

I’ll start with a quoted summary:

From WordPress 6.3 on, the post editor will be iframed if all registered blocks have a Block API version 3 or higher. Adding version 3 support means that the block should work inside an iframe, though the block may still be rendered outside the iframe if not all blocks support version 3.

The goal of version 3 is to load the post editor and site editor inside an iframe. Why an iframe? Please let me explain.

If you’ve ever tried to style in the block editor, you’ll notice that the mobile and tablet previews are rendered in an iframe. This makes styling inside of an iframe possible, but not without hacks. We’ll be using two WordPress functions: wp_deregister_script and wp_register_script.

Here’s the hacky code we used to load styles in the iframe.

function dlx_add_block_stylesheets() {
	// Register the main block style file.
	wp_register_style(
		'dlx_block_styles',
		asset_path( 'wp_blocks.css' ),
		array(),
		dlx_SCRIPT_VERSION,
		'all'
	);

	// Check to see if we need to inject styles into the preview modes.
	$inject_styles = false;
	// This is to check the file exists.
	if ( file_exists( ABSPATH . WPINC . '/css/dist/block-editor/style.min.css') ) {
		$inject_styles = true;
	}

	// If file exists, deregister core style so we can latch onto it.
	if ( $inject_styles ) {
		wp_deregister_style( 'wp-block-editor' );

		// Re-register style with our theme block styles as a dependency.
		wp_register_style(
			'wp-block-editor',
			esc_url( home_url( 'wp-includes/css/dist/block-editor/style.min.css' ) ),
			array( 'dlx_block_styles' )
		);
	}
}
add_action( 'enqueue_block_editor_assets', 'dlx_add_block_stylesheets' );Code language: PHP (php)

Did I mention hacks? We’ve latched on to an existing style that loads in the iframe. First, we register our custom block styles using wp_register_style. After that, we check for the existence of the stylesheet we want to latch onto. Lastly, we used wp_deregister_style to remove the core stylesheet and then re-register it with our styles as a dependency. WordPress will load its block editor stylesheet; when it does, we simply latch onto it as a dependency.

For this next hack, we needed to inject inline styles inside the iframe:

import Sidebar from "./sidebar";
const { registerPlugin } = wp.plugins;
const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost;

const { select } = wp.data;
let stylesIntroduced = false;

registerPlugin("landing-page-gutenberg-template", {
	icon: (
		<svg>
			{ /* code here */ }
		</svg>>
	),
	render: () => {
		// We're piggy backing off a render method here and getting the style settings, adding to it, and updating the core store settings.
		const settingStyles = select( 'core/block-editor' ).getSettings().styles; // This gets the current styles.
		if ( settingStyles && ! stylesIntroduced ) {
			// Add to the settingStyles array.
			settingStyles.push( {
				css: 'table { margin-bottom: 1.5rem; color: #5e5e5e; } th, td { font-family: "Poppins", "Poppins", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-feature-settings: \'kern\', \'liga\', \'ss01\', \'ss02\';padding: 1rem; vertical-align: top; border-top: 1px solid #e6e6e6; }',
			} )
			// Update the settings.
			wp.data.dispatch('core/block-editor').updateSettings( { styles: settingStyles } );
			
			// Make sure we only do it once.
			stylesIntroduced = true;
		}
		return null;
	},
});Code language: JavaScript (javascript)

This code runs in the render argument, which is used to do output to the screen. While in render, we get the block editor styles and add some inline CSS. Finally, we update the core styles.

So why am I showing you all this? Because the iframes make it incredibly difficult to style content in the block editor.

The issue with iframes

The issue with iframes is that they are essentially designed to be independent of the content it is in. Sure, we can style the iframe itself, but styling the contents within the iframe isn’t possible unless there is some way to hook into the iframe output. Easier said than done.

But iframes are here to say, and here’s why: its weakness as basically an independent element is also what makes iframes so great.

Why iframes are needed in the post and site editor

A list of reasons iframes are needed for the block editor
A list of reasons iframes are needed for the block editor

Since the content of the block editor will be wrapped in an iframe, it’s independent of any admin style. In reverse, people loading scripts and styles in the block editor won’t affect the content unless explicitly added.

I encourage you to read about the rational behind iframes, as it is explained rather well in the linked article. It’s a bit of an older article, but many valid points resonate even today.

In general, [iframes] makes the lives of block and theme authors easier because styles from the front-end can be dropped in with very little, if nothing, to adjust.

@ellatrix on WordPress.org

Another point is that the responsive previews are more accurate as they can represent screensizes without many workarounds.

So, to recap, iframes are beneficial because:

  1. They’re style-independent.
  2. They’re easier to manipulate and more responsive when it comes to simulating breakpoints.
  3. Block and theme authors can customize the block editor content appearance without affecting the rest of the block content.
  4. It results in far fewer script and style conflicts.

So, with version 3, how do we get our scripts and styles in the iframe?

Here’s a quick caveat: unless all block plugins on a site have opted-in to version 3, there is no iframe to worry about. So if you’re the holdout still rocking version 2, you don’t have to worry about the iframe until you switch to version 3 (and everyone else has too).

That being said, the common hook enqueue_block_editor_assets is not going away anytime soon.

As of WordPress 6.3, the Post Editor is iframed if all registered blocks have a Block API version 3 or higher and no traditional metaboxes are registered.
Source: GitHub

I tried doing what was suggested to enable the iframe in the post editor. I removed all plugins, enabled the Twenty Twenty Three theme, and changed one activated block plugin to using version 3 of the Block.json API. While I noticed no new iframe in the post editor, I did see it in use in the site editor.

As I learned throughout my research, I decided to take a stab at loading my scripts and styles inside the iframe in the site editor.

The rest of this tutorial will explain the hooks you’ll see when loading scripts and styles for the block editor, particularly in themes.

enqueue_block_editor_assets vs enqueue_block_assets

As of WordPress version 6.3, you can use enqueue_block_assets to load your scripts in the iframe. Enqueue_block_assets will load your scripts and styles in the block editor and inside the iframe for backward compatibility purposes. Prior to WordPress 6.3, enqueue_block_assets was not able to load anything in an iframe.

It is now recommended to stop using enqueue_block_editor_assets and use enqueue_block_assets instead. This, of course, assumes post-6.3 development.

What about previous versions like WordPress 6.2? Or how would someone support an older version and the newest version that encourages all block developers to opt-in to version 3?

As of WordPress 6.3, all assets added through the enqueue_block_assets PHP action will also be enqueued in the iframed Editor. See #48286 for more details.

This is the primary method you should use to enqueue assets for user-generated content (blocks), and this hook fires both in the Editor and on the front end of your site. It should not be used to add assets intended for the Editor UI or to interact with Editor APIs.

Source: GitHub

So, for 6.3 and above, enqueue_block_assets will load on the frontend and in the iframe (if iframe wrapping is enabled). Pre 6.3 enqueue_block_assets was primarily used to enqueue assets needed for the block editor (and the frontend).

A question posted on GitHub brings up a good point. Why would we use enqueue_block_assets if the scripts and styles are also loaded in the frontend? Simply put, you can easily wrap an is_admin in your enqueue_block_assets action to prevent any frontend loading.

Here’s an example I “borrowed” from GitHub to demonstrate only iframe loading (note the is_admin check):

/**
 * Enqueue content assets but only in the Editor.
 */
function example_enqueue_editor_content_assets() {
    if ( is_admin() ) {
        wp_enqueue_script( 
            'example-editor-content-scripts', 
            plugins_url( 'constent-scripts.css', __FILE__ ) );
        }
        wp_enqueue_style( 
            'example-editor-content-styles', 
            plugins_url( 'constent-styles.css', __FILE__ ) );
        }
    }
}
add_action( 'enqueue_block_assets, 'example_enqueue_editor_content_assets' );Code language: PHP (php)

How can I use enqueue_block_assets for older versions of WordPress

Do I have a hack for you? I’m using a class-based structure for this example, but it should work if you change the methods to functions and prefix them appropriately.

This first code sample uses the init action to start things off:

public static function run() {
	$self = new self();
	add_action( 'init', array( $self, 'init' ) );
	return $self;
}Code language: PHP (php)

This is what I refer to as a class runner. I usually call it when the plugins_loaded action is fired.

From there, let’s move into the init callback (I left some rational in the comments):

public function init() {

	// Enqueue block assets.
	add_action( 'enqueue_block_editor_assets', array( $this, 'register_block_editor_scripts' ) ); // This enqueue's like normal.
	add_action( 'enqueue_block_assets', array( $this, 'action_enqueue_block_assets' ) );
        // Since 6.3, enqueue_block_assets runs in an iframe in the site editor. In 6.3,
        // enqueue_block_editor_assets loads outside of editor iframe. Thus, you can check
        // if any of your enqueued script have been registered, and if not,
        // you're on 6.3 and you can enqueue them here. Otherwise, you're on a
        // pre-6.3 version.

}Code language: PHP (php)

You’ll notice I’m using both enqueue_block_editor_assets and enqueue_block_assets together. Why would I do this?

  1. enqueue_block_editor_assets still works well for pre-6.3.
  2. enqueue_block_assets loads first from my tests, both on the frontend and the site editor.

As one contributor mentioned on GitHub:

  1. enqueue_block_assets should be used to load editor stylesheets and scripts. Please note enqueue_block_assets can run on the frontend if there is no is_admin check.
  2. enqueue_block_editor_assets should include scripts and styles that can run in the block editor, but not affect any content styles.
  3. If using enqueue_block_editor_assets, you’ll want to prefix your CSS with: .editor-styles-wrapper or .wp-block*. This is to prevent style leakage.

Let’s move on to the rest of the code:

/**
 * Enqueue block assets inside an iframe.
 */
public function enqueue_block_editor_iframe() {
	if ( ! is_admin() ) {
		return;
	}

	// We'll pick on a script that's enqueued using `enqueue_block_editor_assets`.
	if ( wp_script_is( 'alerts-dlx-block', 'enqueued' ) ) {
		// If this script was enqueued, it means `enqueue_block_editor_assets` has run, therefore we are not in an iframe.
		// If the script is not enqueued, we're likely in the iframe, so we can enqueue or assets as normal.
		return;
	}

	// Register styles for the iframe in 6.3.
	wp_enqueue_style(
		'alerts-dlx-block-editor',
		Functions::get_plugin_url( 'dist/alerts-dlx-block-editor.css' ),
		array(),
		Functions::get_plugin_version(),
		'all'
	);
	wp_enqueue_style(
		'alerts-dlx-block-editor-styles',
		Functions::get_plugin_url( 'build/alerts-dlx.css' ),
		array( 'alerts-dlx-block-editor' ),
		Functions::get_plugin_version(),
		'all'
	);
}

/**
 * Enqueue scripts for the block editor.
 */
public function enqueue_block_editor_scripts_styles() {

	// Enqueue scripts and styles that need to go in the iframe.
	wp_enqueue_style(
		'alerts-dlx-block-editor',
		Functions::get_plugin_url( 'dist/alerts-dlx-block-editor.css' ),
		array(),
		Functions::get_plugin_version(),
		'all'
	);
	wp_enqueue_style(
		'alerts-dlx-block-editor-styles',
		Functions::get_plugin_url( 'build/alerts-dlx.css' ),
		array( 'alerts-dlx-block-editor' ),
		Functions::get_plugin_version(),
		'all'
	);

	wp_enqueue_style(
		'alerts-dlx-block-editor-styles-lato',
		Functions::get_plugin_url( 'dist/alerts-dlx-gfont-lato.css' ),
		array(),
		Functions::get_plugin_version(),
		'all'
	);

	wp_enqueue_style(
		'alerts-dlx-common',
		Functions::get_plugin_url( 'dist/alerts-dlx-common.css' ),
		array( 'alerts-dlx-block-editor-styles-lato' ),
		Functions::get_plugin_version(),
		'all'
	);

	wp_enqueue_script(
		'alerts-dlx-block',
		Functions::get_plugin_url( 'build/alerts-dlx.js' ),
		array(),
		Functions::get_plugin_version(),
		true
	);

	wp_localize_script(
		'alerts-dlx-block',
		'alertsDlxBlock',
		array(
			'font_stylesheet' => Functions::get_plugin_url( 'dist/alerts-dlx-gfont-lato.css' ),
		)
	);
}Code language: PHP (php)

I’ve created a nifty flowchart to demonstrate what’s happening:

Example flowchart showing script loading
Use Enqueue Status to Determine if We’re on Older Versions of WordPress.

The theory behind this is that enqueue_block_assets loads first, so it has a chance to register its scripts. After that, enqueue_block_editor_assets is run, which registers its scripts/styles.

When in an iframe, enqueue_block_editor_assets is not run. The only thing that should be called in an iframe is enqueue_block_assets. So a simple handle check will eliminate any redunancy, and set you up for iframes in the site editor and the block editor in the future when iframes apply to the post content block editor.

What’s the status of being able to add iframe’d content?

iframe's code sample of HTML output.
An Example of AlertsDLX Loading Styles in the Site Editor Iframe

As mentioned, all blocks on the site must have opted-in to version 3 in Block.json. I attempted this locally with 6.3, with one block opted-in, and I used enqueue_block_assets to enqueue my scripts.

No matter how I tried, I couldn’t get an iframe to load in the post editor, but I could add my styles to the site editor’s iframe. This leads me to conclude that either the block editor isn’t ready for iframes yet, or I’m doing something wrong (not out of the question).

Conclusion

Getting scripts and styles into the post editor iframe is coming, but not until all block plugins on a site have opted-in to version 3 in Block.json. This could take a while for mass adoption.

Switching to version 3 is simple. It’s just a single flag. However, the caveats, backward compatibility, and the amount of community opt-in will certainly make this change rather ambitious but worthwhile.

That being said, what do you think of this API change? Are you excited to finally be able to load assets in the content iframe? Please let me know in the comments.

Like this tutorial? There's more like it. Subscribe today!

Name(Required)

Ronald Huereca
By: Ronald Huereca
Published On: on August 26, 2023

Ronald Huereca founded DLX Plugins in 2022 with the goal of providing deluxe plugins available for download. Find out more about DLX Plugins, check out some tutorials, and check out our plugins.

Leave Your Valuable Feedback

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

Shopping Cart
  • Your cart is empty.
Scroll to Top