Following the updated norms in WordPress Gutenberg, custom blocks should now be registered using a block.json file. In this guide we’ll go into details on how to go about this.

Updated method of registering blocks

Since WordPress version 5.8 using the block.json metadata file has been the canonical way to register custom blocks. There are several benefits; such as less code to write. Previously you had to do block registration twice – once in JavaScript and once in PHP. You also had to manually enqueue your scripts and styles for it. Another benefit is in performance for themes that support lazy loading assets. Your scripts and styles for your block is only enqueued when the block is present on the page.

What follows is a guide in how to add a block.json file and what it should contain, and the new method of registering custom blocks. This will replace the old method described in my guide Create Custom Gutenberg Block – Part 2: Register block.

Setting up the files

For this example I’m creating a custom block in a WordPress plugin. The content of my plugin looks like this:

I won’t go into detail on how to set up webpack.config.js and package.json as this is covered in my guide Complete Guide in Setting up a Development Environment for Gutenberg. The node_modules folder comes when you set up the build environment, and the build folder has been generated from the command for building my files within the src folder. The most important parts are these:

The plugin.php contains the plugin registration and will later on also contain the necessary PHP parts for registering the block.

The block.json file which we will look at momentarily.

Finally the block code itself is inside the src folder – here I have an index.js that contains the Javascript block registration, and a save.js and edit.js file.

Let’s start with the block.json file.

Setting up block.json

Previously we would add most properties for the block inside our block registration code block, typically inside index.js. But in order to take advantage of the shared metadata we move most of it into block.json, such as block name, title, description, keywords, category, supports and attributes.

This is an example of block.json content for a basic custom block:

	"$schema": "",
	"apiVersion": 2,
	"name": "awp/custom-block",
	"title": "AWP Custom Block",
	"description": "Example of custom block with block.json",
	"category": "design",
	"textdomain": "awp",
	"supports": {
		"align": ["wide"]
	"attributes": {
		"align": {
			"type": "string",
			"default": "wide"
		"title" : {
			"type": "string"
	"editorScript": "file:build/index.js"

If you’ve worked with custom blocks before, most of the keywords should be familiar to you.

$schema” and “apiVersion” are nice to add as they provide useful information for editors and informs WordPress which API REST version to use (version 2 is the most recent, introduced in WordPress 5.6). “textdomain” allows for translation of your block.

An important part is the last line; “editorScript“. This line tells WordPress which script to enqueue in the editor for block type definition. As the keyword says, this file will only be enqueued in the context of the editor. This is where we define the path to the build of our block registration, which for my plugin resides in build/index.js.

There are several other properties available, for example “script” for enqueuing script files in both editor and frontend, “style” for enqueuing stylesheet for both editor and frontend, and “editorStyle” for styles in the editor only.

A complete overview of possible properties in block.json can be found here.

Registering the block in JavaScript

Registering the block in JavaScript has not changed. We still use registerBlockType() found in wp.blocks. However we now want to reference information from our block.json instead of repeating most of it inside this function call.

You can follow most of the guide in Create Custom Gutenberg Block – Part 2: Register block, and just make these small changes.

First off we import block.json, and we use the “name” as defined in that file for registerBlockType(). We don’t need to specify properties for e.g. supports, attributes, etc., as this is automatically parsed from block.json. So the most basic custom block registration could be just this:

 * WordPress dependencies
import { registerBlockType } from '@wordpress/blocks';

 * Internal dependencies
import name from '../block.json';
import edit from './edit';
import save from './save';

registerBlockType(name, {

And that’s all you need for block registration in JavaScript. I won’t go into showing edit.js and save.js as this is the block code part that is up to you, and unrelevant for this guide.

Registering the block in PHP using block.json

Let’s move on to PHP. I’m adding this code into my main plugin file, plugin.php.

Previously we would register the block using the function register_block_type(). This method still works, but does not utilize block.json. Instead we use the new function register_block_type_from_metadata(). It does pretty much the same thing as the previous method, except that it will parse block.json for all the information it needs.

We’ll hook a function onto the hook init, and call register_block_type_from_metadata():

add_action( 'init', 'awp_custom_block_init' );
function awp_custom_block_init() {
	register_block_type_from_metadata( __DIR__ );

As first parameter to the function you need to provide the path to where your block.json file resides. In this case block.json resides in root plugin folder, so we provide the __DIR__ constant that refers to the folder this file resides in, which is the same as block.json.

You can provide arguments to the array as the second parameter, just like you did with register_block_type(). For example if your block is server-rendered you can provide render_callback, like so:

add_action( 'init', 'awp_custom_block_init' );
function awp_custom_block_init() {
	register_block_type_from_metadata( __DIR__, [
		'render_callback' => 'awp_custom_block_render',
	] );

function awp_custom_block_render( $args, $content ) { ... }

A complete overview of possible arguments as second parameter can be found here. But remember that you no longer need to provide most arguments, such as attributes (which you had to previously for server-rendered blocks), since this happens automatically from your block.json!

And this is all PHP code you will need for your custom block. You no longer need to manually enqueue the scripts and styles as this is handled automatically via block.json.

And that’s it!

This is all you need! After building your files and activating the plugin, the block should be available in the block editor!


If you’re wondering how to handle translation of your block using block.json, I have created a guide that covers just that.