How to Generate a Block Manifest to Improve Block Performance

In WordPress 6.7, block manifests were introduced to improve overall block performance. This was iterated on in WordPress 6.8, making block registration much easier. In this tutorial, I’ll explain why block manifests are needed, and how to generate them.

What are block manifests?

Screenshot of a Block Manifest File

A block manifest is a file that contains all of the information about your blocks in PHP format. Think of it as one large file that contains all of your block.json data for every block registered.

In a normal operation, your block.json file is read and parsed numerous times in WordPress, which can hamper performance.

For each page load, the following operations are executed by WordPress:

  • Scan folders for block.json files.
  • When a block.json file is found, read its contents into memory.
  • Call json_decode on the file contents to convert it to a PHP array.
  • Register the block using the decoded data.

Source: Improve PHP Performance for Block Registration.

Block Manifests solve this problem by helping you register a group of blocks as a collection. Once registered, your block.json files are consolidated in a blocks-manifest.php file, which can be read in once as needed.

Since the file is in PHP format, it can be read in programmatically and parsed server-side, thus improving read/write and parsing performance.

Block manifests for single-block plugins

Should block manifests be used for single-block plugins? Yes, and no.

In principle there is, yes. The benefit is that it would still avoid parsing the `block.json` file via `json_decode()`, which is somewhat expensive.

For a single block the real performance impact is probably negligible, but when things add up (e.g. 20+ plugins with a single block), it’s still useful to use a block metadata collection even for plugins with a single block.

So certainly a best practice even in those cases.
Citation Image
WordPress Core Committer

In principle, if you have a single-block plugin, you don’t need block manifests, but it is a good idea to do so.

Generating a block manifest

Flowchart of Block Manifest Generation

To generate a block manifest, you’ll need to update your build tools slightly to use the latest version of @wordpress/scripts.

Let’s walk through how I would update my plugin WP Plugin Info Card, which has 4 blocks, to use block manifests for registration.

Step 1: Update the @wordpress/scripts package

You’ll need to update your @wordpress/scripts package to version 30.20.0 or higher.

First, I’ll navigate to my plugin via command line and uninstall @wordpress/scripts.

npm uninstall @wordpress/scripts --legacy-peer-depsCode language: Bash (bash)

Once that’s done, I’ll reinstall it using this command:

npm install @wordpress/scripts@^30.20.0 --save-dev --legacy-peer-depsCode language: Bash (bash)

Step 2: Update your package.json to include manifests

Here’s how my build steps currently look in my package.json file.

"scripts": {
  "start": "wp-scripts start wppic-blocks=./src/index.js --env mode=development",
  "dev": "wp-scripts start wppic-blocks=./src/index.js --env mode=development",
  "build": "wp-scripts build wppic-blocks=./src/index.js --env mode=production",
  "zip": "wp-scripts plugin-zip"
},Code language: JSON / JSON with Comments (json)

In order to generate the block manifest, I’ll add a --blocks-manifest flag into my package.json file.

"scripts": {
  "start": "wp-scripts start wppic-blocks=./src/index.js --blocks-manifest --env mode=development",
  "dev": "wp-scripts start wppic-blocks=./src/index.js --blocks-manifest --env mode=development",
  "build": "wp-scripts build wppic-blocks=./src/index.js --blocks-manifest --env mode=production",
  "zip": "wp-scripts plugin-zip"
},Code language: JSON / JSON with Comments (json)

After that, I’ll test using npm run build to see if my block manifest file is generated.

Generated Blocks Manifest File

Now that the file is generated, I need to register the block collection.

Registering a block collection

To register blocks, you would normally use function register_block_type. Here’s an example from WP Plugin Info Card:

register_block_type(
	Functions::get_plugin_dir( 'build/blocks/PluginInfoCard/block.json' ),
	array(
		'render_callback' => array( $this, 'info_card_render' ),
	)
);
register_block_type(
	Functions::get_plugin_dir( 'build/blocks/PluginInfoCardQuery/block.json' ),
	array(
		'render_callback' => array( $this, 'info_card_query_render' ),
	)
);
register_block_type(
	Functions::get_plugin_dir( 'build/blocks/SitePluginsCardGrid/block.json' ),
	array(
		'render_callback' => array( $this, 'site_plugin_card_grid_render' ),
	)
);
register_block_type(
	Functions::get_plugin_dir( 'build/blocks/PluginScreenshotsInfoCard/block.json' ),
	array(
		'render_callback' => array( $this, 'site_plugin_screenshots' ),
	)
);Code language: PHP (php)

To update this block, I’ll be using WP 6.7 function wp_register_block_metadata_collection and WP 6.8 function wp_register_block_types_from_metadata_collection.

In order to make this compatible with previous versions of WordPress, I’ll be adding a few conditionals and function checks.

if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
	wp_register_block_types_from_metadata_collection( Functions::get_plugin_dir( 'build' ), Functions::get_plugin_dir( 'build/blocks-manifest.php' ) );
	return;
} else {
	if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
		wp_register_block_metadata_collection( Functions::get_plugin_dir( 'build' ), Functions::get_plugin_dir( 'build/blocks-manifest.php' ) );
	}
	$manifest_data = require Functions::get_plugin_dir( 'build/blocks-manifest.php' );
	foreach ( array_keys( $manifest_data ) as $block_type ) {
		register_block_type( __DIR__ . "/build/{$block_type}" );
	}
}Code language: PHP (php)
  1. In the above example, the function wp_register_block_types_from_metadata_collection is called if the user is above WP 6.8. This function both registers the collection, and individual blocks. So in theory this is a one-and-done operation.
  2. If the user is on WP 6.7, the function wp_register_block_metadata_collection is called to register the collection. You’ll still need to register the blocks individually.
  3. Lastly, the blocks are registered individually (if on WP 6.7 or lower).

You may have noticed I’m using a static method to find my files and directories. This is detailed in an article I wrote about useful utility functions you can add to your plugin.

Dealing with dynamic blocks and render_callback

If you have a dynamic block and use render_callback to output your block to the frontend, you’ll have to modify the registration arguments at run-time.

Function wp_register_block_types_from_metadata_collection both registers the collection and blocks, but it doesn’t provide a render_callback argument. This means if you’re using block-manifests with render_callback, your blocks will no longer display.

Here’s how to get render_callback back.

Using filter block_type_metadata_settings, we can modify the registration arguments for the block when it’s registered.

Here’s the full function when registering my dynamic blocks:

$render_callbacks = array(
	'wp-plugin-info-card/wp-plugin-info-card'   => array( $this, 'info_card_render' ),
	'wp-plugin-info-card/wp-plugin-info-card-query' => array( $this, 'info_card_query_render' ),
	'wp-plugin-info-card/site-plugin-card-grid' => array( $this, 'site_plugin_card_grid_render' ),
	'wp-plugin-info-card/plugin-screenshots-info-card' => array( $this, 'site_plugin_screenshots' ),
);

add_filter(
	'block_type_metadata_settings',
	function ( $settings, $metadata ) use ( $render_callbacks ) {
		if ( isset( $render_callbacks[ $metadata['name'] ] ) ) {
			$settings['render_callback'] = $render_callbacks[ $metadata['name'] ];
		}
		return $settings;
	},
	10,
	2
);

if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
	wp_register_block_types_from_metadata_collection( Functions::get_plugin_dir( 'build' ), Functions::get_plugin_dir( 'build/blocks-manifest.php' ) );
	return;
} else {
	if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
		wp_register_block_metadata_collection( Functions::get_plugin_dir( 'build' ), Functions::get_plugin_dir( 'build/blocks-manifest.php' ) );
	}
	$manifest_data = require Functions::get_plugin_dir( 'build/blocks-manifest.php' );
	foreach ( array_keys( $manifest_data ) as $block_type ) {
		register_block_type( __DIR__ . "/build/{$block_type}" );
	}
}Code language: PHP (php)

Here’s what’s going on:

  1. I created an array with my blocks and their callbacks.
  2. I used an inline filter declaration and modified the block settings to include my callbacks.
  3. Block registration proceeds as normal, with the filter hooking in during registration.

With this update, my blocks now render correctly on the frontend.

Conclusion

Block manifests increase performance by not having to parse and render numerous block.json files per plugin.

I explained how to update a block plugin to use block manifests and how to update your build scripts.

There are some caveats when registering the blocks, notably if using render_callback for dynamic blocks.

Like the tutorial you just read? There's more like it.

There's more where that came from. Enter your email and I'll send you more like it.

This field is for validation purposes and should be left unchanged.

Ask a Question or Leave Feedback

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