Block.json Explained for WordPress Block Plugin Developers

JSON in code view

Block.json is an essential file for block development, describing your block so that WordPress and others know how to handle it. This can include loading the correct script when your block is displayed, or displaying a proper icon on the WordPress Plugin Directory next to your block.

Whether you’re new to block development or a seasoned developer looking to streamline your workflow, understanding block.json is crucial as it’s the foundation for most blocks. Let’s dive in and explore block.json in detail.

Table of Contents

The importance of block.json

Puzzle Pieces
Block.json can be like a puzzle

The block.json file contains almost all of the information needed about your block. It streamlines the registration process by consolidating block settings, attributes, and dependencies into a single, easy-to-manage file. This allows portable metadata that can be used anywhere your block is displayed.

By leveraging block.json, developers can efficiently define and organize block properties, support internationalization, and improve the overall performance and user experience of their blocks. Understanding and utilizing block.json is essential for creating robust, scalable, and future-proof block plugins.

Block.json file location and manifests

It’s generally recommended to have each block in a dedicated subfolder within an src folder in your plugin.

For example, a Gravatar block I created from scratch using @wordpress/create-block has a structure as follows:

.
└── dlx-gravatar-block
    ├── build
    ├── src
    │   └── dlx-gravatar-block
    │       ├── block.json
    │       └── ~other block files
    ├── dlx-gravatar-block.php
    └── readme.txtCode language: AsciiDoc (asciidoc)

Notice how there’s a subfolder in src called dlx-gravatar-block, and within that folder is the block.json file.

There used to be a different structure between single and multiple blocks, as single blocks don’t need a nested structure. However, since it is challenging to re-architect a single-block plugin into a multi-block one, it is recommended to assume a multi-block structure from the start. The growing popularity of block manifests and overall performance gains in the block editor reinforce this.

To generate the above structure, I used npx package @wordpress/create-block to scraffold my block. An example is below:

npx @wordpress/create-block@latest --namespace=dlxplugins --title="DLX Gravatar Block" --wp-scripts --variant=dynamicCode language: Bash (bash)

This command generates a new plugin, sets up all the build scripts, and auto-places the block.json file for me to edit later.

When the build scripts are run, a build folder is created. Populated in this file is a similar structure to your src folder, albeit with compiled assets.

.
└── dlx-gravatar-block
    ├── build
    │   ├── blocks-manifest.php
    │   └── dlx-gravatar-block
    │       ├── block.json
    │       └── ~other block files
    ├── src
    ├── dlx-gravatar-block.php
    └── readme.txtCode language: AsciiDoc (asciidoc)

Here’s an example of what’s in blocks-manifest.php:

<?php
// This file is generated. Do not modify it manually.
return array(
	'dlx-gravatar-block' => array(
		'$schema'      => 'https://schemas.wp.org/trunk/block.json',
		'apiVersion'   => 3,
		'name'         => 'dlxplugins/dlx-gravatar-block',
		'version'      => '0.1.0',
		'title'        => 'DLX Gravatar Block',
		'category'     => 'widgets',
		'icon'         => 'smiley',
		'description'  => 'Example block scaffolded with Create Block tool.',
		'example'      => array(),
		'supports'     => array(
			'html' => false,
		),
		'textdomain'   => 'dlx-gravatar-block',
		'editorScript' => 'file:./index.js',
		'editorStyle'  => 'file:./index.css',
		'style'        => 'file:./style-index.css',
		'render'       => 'file:./render.php',
		'viewScript'   => 'file:./view.js',
	),
);
Code language: PHP (php)

In my main plugin file, the block.json metadata is read by parsing the generated block manifest.

function dlxplugins_dlx_gravatar_block_block_init() {
	/**
	 * Registers the block(s) metadata from the `blocks-manifest.php` and registers the block type(s)
	 * based on the registered block metadata.
	 * Added in WordPress 6.8 to simplify the block metadata registration process added in WordPress 6.7.
	 *
	 * @see https://make.wordpress.org/core/2025/03/13/more-efficient-block-type-registration-in-6-8/
	 */
	if ( function_exists( 'wp_register_block_types_from_metadata_collection' ) ) {
		wp_register_block_types_from_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' );
		return;
	}

	/**
	 * Registers the block(s) metadata from the `blocks-manifest.php` file.
	 * Added to WordPress 6.7 to improve the performance of block type registration.
	 *
	 * @see https://make.wordpress.org/core/2024/10/17/new-block-type-registration-apis-to-improve-performance-in-wordpress-6-7/
	 */
	if ( function_exists( 'wp_register_block_metadata_collection' ) ) {
		wp_register_block_metadata_collection( __DIR__ . '/build', __DIR__ . '/build/blocks-manifest.php' );
	}
	/**
	 * Registers the block type(s) in the `blocks-manifest.php` file.
	 *
	 * @see https://developer.wordpress.org/reference/functions/register_block_type/
	 */
	$manifest_data = require __DIR__ . '/build/blocks-manifest.php';
	foreach ( array_keys( $manifest_data ) as $block_type ) {
		register_block_type( __DIR__ . "/build/{$block_type}" );
	}
}
add_action( 'init', 'dlxplugins_dlx_gravatar_block_block_init' );Code language: PHP (php)

The function wp_register_block_types_from_metadata_collection is used in WP 6.8+ and registers all blocks in a collection at once. Since the blocks-manifest.php file has a list of blocks in a collection, you can iterate through them programmatically and call register_block_type to register your blocks.

In summary for this section:

  • Each block should be placed in its own folder, each having a block.json file.
  • Blocks are read and registered using blocks-manifest.php, located in your build folder.

For further reading, please see my article on why block manifests are useful.

Generating a block.json File

There are several ways to generate a block.json file.

Regardless of the technique used to generate the block.json file, it is highly recommended to be familiar with all of the various parameters block.json entails. Let’s go over the block.json structure in detail.

Block.json structure

One of the benefits of using block.json, is that it has a schema. This aids in auto-completion and troubleshooting errors.

Here are the sections you’ll find in a typical block.json file. We’ll be going over these in detail.

  • apiVersion: The version of the block.json API you will be using.
  • name: The unique identifier for the block.
  • title: The display title of the block.
  • category: The category under which the block is grouped in the block inserter.
  • icon: The icon displayed in the block inserter.
  • description: A short description of the block.
  • keywords: An array of search terms to help users find the block.
  • supports: Features that the block supports (e.g., alignment, anchor, color).
  • attributes: The properties of the block, defining its data structure.
  • styles: An array of style variations for the block.
  • editorScript: The script file loaded in the block editor.
  • script: The script file loaded on the front end and in the editor.
  • editorStyle: The stylesheet loaded in the block editor.
  • style: The stylesheet loaded on the front end.
  • render: The file to render your block (useful for single-block plugins).
  • textdomain: The text domain for translations.
  • example: Example block data used in the block preview.
  • transforms: Block transformation rules.
  • parent: Specifies the parent block, if any.
  • children: Specifies child blocks, if any.
  • variations: Different variations of the block.
  • deprecated: Deprecated versions of the block.

Here’s a sample block.json, which illustrates some of the properties:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"title": "Gravatar Block",
	"apiVersion": 3,
	"name": "dlxplugins/gravatar-block",
	"category": "media",
	"icon": "admin-users",
	"description": "An Gravatar block.",
	"keywords": [
	  "avatar",
	  "gravatar"
	],
	"version": "1.0.0",
	"textdomain": "dlx-gravatar-block",
	"attributes": {
		"gravatarHash": {
			"type": "string",
			"default": ""
		},
		"gravatarSize": {
			"type": "string",
			"default": ""
		},
		"align": {
			"type": "string",
			"default": "none"
		}
	},
	"supports": {
	  "anchor": true,
	  "align": true,
	  "className": true
	},
	"editorScript": "dlx-gravatar-block-editor",
	"editorStyle": "dlx-gravatar-block-editor"
  }Code language: JSON / JSON with Comments (json)

Let’s go over the various properties and how they are used in the block.json file.

The ‘apiVersion’ property

You’ll find versions 2 and 3 in the wild. Version 3 is if you intend everything to run in an iframe when your block is rendered. If you’re having trouble getting your block to work with third-party libraries in the block editor, you’ll find that version 2 is a safer option.

Please note that as of this writing, if any block is in version 2, the block editor will not render in an iframe.

Check out my article on block.json version 3 and iframes.

The ‘name’ property

The “name” option in Block.json is the namespace for your block (e.g., has/click-to-share). Make sure this is unique so that there aren’t any conflicts with other block plugins.

The ‘title’ property

Example of a Block Title
Example of a Block Title

The title is the name of your block. It is used in the block preview and the block sidebar.

It’s also used on WordPress.org when listing out your blocks.

AlertsDLX .org Plugin Page With Blocks
AlertsDLX WordPress.org Plugin Page With Blocks

This property is crucial for providing a clear and concise description of the block’s purpose, making it easily identifiable for users when browsing or searching for blocks. A well-chosen title enhances usability and ensures that users can quickly understand and select the block that best fits their needs.

The ‘category’ property

AlertsDLX Category Grid in the Block Editor
AlertsDLX Category Grid in the Block Editor

The block category is where you’d like your block to show up when a user hits the “+” button to insert a block in the block editor.

The default category types are as follows:

  • text
  • common
  • media
  • design
  • widgets
  • theme
  • embed

Source: How to Create a Custom Blocks Category for WordPress.

You can also create your own categories using the hook block_categories_all.

add_action( 'block_categories_all', 'prefix_add_block_category', 10, 2 );
function prefix_add_block_category( $categories, $post ) {
	return array_merge(
		$categories,
		array(
			array(
				'slug'  => 'wp-plugin-info-card',
				'title' => __( 'WP Plugin Info Card', 'wp-plugin-info-card' ),
			),
		)
	);
}Code language: PHP (php)
Plugin Info Card Category
Plugin Info Card Category

Here’s how you would add an icon to the category via JavaScript so it shows in the block editor.

import InfoCardIcon from './blocks/components/InfoCardIcon';

import './blocks/PluginInfoCard/block';
import './blocks/PluginInfoCardQuery/wppic-query';
import './blocks/SitePluginsCardGrid/block';
import './blocks/PluginScreenshotsInfoCard/block';

/**
 * Add Block Category Icon.
 */
( function() {
	const InfoCardSVG = <InfoCardIcon fill="#DB3939" />;
	wp.blocks.updateCategory( 'wp-plugin-info-card', { icon: InfoCardSVG } );
}() );
Code language: JavaScript (javascript)

The ‘icon’ property

WordPress.org Icons for AlertsDLX
WordPress.org Icons for AlertsDLX

The icon property in block.json specifies the visual icon that represents the block in on the WordPress plugin directory. This property enhances the user experience by providing a visual cue that helps users quickly identify the block’s functionality. The icon can be defined using a Dashicon class, an SVG element, or a custom URL to an image.

If you’re including an SVG, convert all of the double quotes to single quotes. For example:

"icon": "<svg aria-hidden='true' focusable='false' data-prefix='fas' data-icon='share-alt' className='svg-inline--fa fa-share-alt fa-w-14' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'><path fill='currentColor' d='M352 320c-22.608 0-43.387 7.819-59.79 20.895l-102.486-64.054a96.551 96.551 0 0 0 0-41.683l102.486-64.054C308.613 184.181 329.392 192 352 192c53.019 0 96-42.981 96-96S405.019 0 352 0s-96 42.981-96 96c0 7.158.79 14.13 2.276 20.841L155.79 180.895C139.387 167.819 118.608 160 96 160c-53.019 0-96 42.981-96 96s42.981 96 96 96c22.608 0 43.387-7.819 59.79-20.895l102.486 64.054A96.301 96.301 0 0 0 256 416c0 53.019 42.981 96 96 96s96-42.981 96-96-42.981-96-96-96z'></path></svg>",Code language: JSON / JSON with Comments (json)

The SVG must be kept simple. I’ve noticed on .org that if you have elements other than path, the SVG won’t render correctly. Also, any style attributes are ignored. Find out more about setting a block.json icon property.

Specifying the SVG here won’t change the icon in the block editor, so it’s still beneficial to specify an icon for the block when you register your block via JavaScript.

To register your icon in JavaScript, you must convert your SVG to a component and include it when registering your block.

import { registerBlockType, createBlock } from '@wordpress/blocks';
import { InnerBlocks } from '@wordpress/block-editor';
import metadata from './block.json';
import BootstrapLogo from '../components/icons/BootstrapLogo';
import Edit from './edit';

registerBlockType( metadata, {
	edit: Edit,
	save() {
		return <InnerBlocks.Content />;
	},
	icon: <BootstrapLogo />,
	/* rest of the code */
} );
Code language: JavaScript (javascript)
Custom Icon Shown in the Block Editor
Custom Icon Shown in the Block Editor

To create a component from your SVG, I recommend tool SVGR Playground.

The ‘description’ property

The Description of a Block in the Block Editor
The Description of a Block in the Block Editor

The description property in block.json provides a short summary of your block’s function. This text appears in the block inserter and block sidebar, helping users understand the purpose and functionality of your block at a glance.

Example of a Description in the Block Sidebar
Example of a Description in the Block Sidebar

Your description also appears on .org.

Description Properties Shown on WordPress.org
Description Properties Shown on WordPress.org

The ‘keywords’ property

Searching for Alerts in the Block Editor
Searching for Alerts in the Block Editor

This is a JSON array of keywords you’d like your block to show up for. Here’s an example of one from AlertsDLX.

"keywords": ["alert", "info", "hint", "success", "error", "notice", "notification", "warning", "bootstrap"],Code language: JSON / JSON with Comments (json)

The ‘supports’ property

The supports property is an ever-evolving property, which is laid out in detail in the developer handbook on Supports.

Here’s a quick example that will enable an anchor, alignment, and a CSS class name:

"supports": {
	"anchor": true,
	"align": true,
	"className": true
},Code language: JSON / JSON with Comments (json)

With the above supports example, you don’t have to add a block attribute for className. The anchor and align parameters require block.json attributes to be defined.

"attributes": {
	"align": {
		"type": "string",
		"default": "none"
	},
	"anchor": {
		"type": "string",
		"default": ""
	}
},Code language: JSON / JSON with Comments (json)

To output the alignment and class name, you’d use function get_block_wrapper_attributes. Here’s an example of outputting the anchor:

<?php

$block_wrapper_attributes = get_block_wrapper_attributes(
	array(
		'id' => $attributes['anchor'],
	)
);
?>
<p <?php echo wp_kses_post( $block_wrapper_attributes ); ?>>
	Sample block output
</p>Code language: PHP (php)

Here’s the resulting markup:

<p class="alignwide sample-css-classname wp-block-dlxplugins-dlx-sample-supports-block" id="introduction">
	Sample block output
</p>Code language: HTML, XML (xml)

Let’s use a different example to show how to add padding and margin support to a block.

We’d add this to the supports parameter in block.json:

"supports": {
	"html": false,
	"anchor": true,
	"align": true,
	"className": true,
	"spacing":{
		"padding": true,
		"margin": true
	}
},Code language: JSON / JSON with Comments (json)

For block.json, you’d need to declare a styles attribute:

"attributes": {
	"align": {
		"type": "string",
		"default": "none"
	},
	"anchor": {
		"type": "string",
		"default": ""
	},
	"styles": {
		"type": "object",
		"default": {
			"padding": {
				"top": "0",
				"right": "0",
				"bottom": "0",
				"left": "0"
			},
			"margin": {
				"top": "0",
				"right": "0",
				"bottom": "0",
				"left": "0"
			}
		}
	}
},Code language: JSON / JSON with Comments (json)

Here would be the resulting output:

<p style="padding-top:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--40);padding-left:var(--wp--preset--spacing--40);padding-right:var(--wp--preset--spacing--40);margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);margin-left:var(--wp--preset--spacing--20);margin-right:var(--wp--preset--spacing--20);" class="alignwide sample-css-classname wp-block-dlxplugins-dlx-sample-supports-block" id="introduction">
	Sample block output
</p>Code language: HTML, XML (xml)

There are many other supports parameters that you may find useful. To see everything you can add, please refer to the Block Editor Handbook on supports.

I’ve found various support parameters like dimension and typography support to be limited in terms of implementation, and I’ve seen many blocks include their own custom components for this functionality.

The ‘attributes’ property

Attributes are the data the block stores as users interact with your block. Think of them as variables you can save to and read from. For example, if a button block has a background color, that background color is stored as an attribute.

The current version of block.json supports the following attribute types:

  • null
  • boolean
  • object
  • array
  • string
  • integer
  • number
  • richtext

For example, here are two attributes I use for my sample Gravatar Block:

"attributes": {
	"gravatarHash": {
		"type": "string",
		"default": ""
	},
	"gravatarSize": {
		"type": "number",
		"default": 96
	}
},Code language: JSON / JSON with Comments (json)

Each attribute has a “name”, a type, and a default. These can get more complicated with static blocks, which I’ll go over in a minute.

The more common attributes, string, integer, and number, are pretty straightforward. The only caveat is to make sure to save numbers as numbers, else they won’t save.

The attribute type boolean is often used for Toggles, and can be true or false.

For an array, it’s for saving more complex data, like, say, for example, I wanted to have a gallery of Gravatar blocks for team members:

"attributes": {
	"teamGravatars": {
		"type": "array",
		"default": [
			{
				"name": "John Doe",
				"gravatarHash": "aca52b16e75868bc4c6533bdeac750ef",
				"gravatarSize": 96
			}
		]
	}
},Code language: JSON / JSON with Comments (json)

If it’s an object type, you can save all the necessary parameters in just one attribute instead of several:

"attributes": {
	"userGravatar": {
		"type": "object",
		"default": {
			"name": "John Doe",
			"gravatarHash": "aca52b16e75868bc4c6533bdeac750ef",
			"gravatarSize": 96
		}
	}
}Code language: JSON / JSON with Comments (json)

Attributes and how they’re implemented can differ between static and dynamic blocks, so let’s go over static blocks and their attributes.

Static Block Attributes

Static blocks are rendered by the save function when registering a block. This saves the HTML content to the block, so that it is output to the front end.

In this example, I have two attributes, both of which are defined as a string:

  • gravatarHash – Given a default of an empty string.
  • gravatarSize – Given a default of 96.

As users interact with my block, I output the main interface:

// If there's an avatar URL, display it.
if ( gravatarHash ) {
	return (
		<div { ...blockProps }>
			{ toolbar }
			<img
				src={ `https://secure.gravatar.com/avatar/${ gravatarHash }?s=${ gravatarSize }` }
				data-gravatar-hash={ gravatarHash }
				data-gravatar-size={ gravatarSize }
				alt={ __( 'Gravatar', 'dlx-gravatar-block' ) }
				width={ gravatarSize }
				height={ gravatarSize }
			/>
		</div>
	);
}Code language: JavaScript (javascript)

Everything is directly read from the attributes, and nothing is calculated on the fly. Since this will be stored in the block’s HTML, it needs to be static.

In the save function, I output very similar HTML:

/**
 * Internal dependencies
 */
import Edit from './edit';
import metadata from './block.json';

registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,
	save( { attributesv } ) {
		const { gravatarHash, gravatarSize } = attributes;

		return (
			<div { ...useBlockProps.save({ className: 'dlx-gravatar-block' }) }>
				{ gravatarHash ? (
				<img
					src={ `https://secure.gravatar.com/avatar/${ gravatarHash }?s=${ gravatarSize }` }
					data-gravatar-hash={ gravatarHash }
					data-gravatar-size={ gravatarSize }
					alt="Gravatar"
					width={ gravatarSize }
					height={ gravatarSize }
				/>
				) : null }
			</div>
		);
} } );Code language: JavaScript (javascript)

On the frontend, the saved HTML will output.

<div class="wp-block-dlxplugins-dlx-gravatar-block dlx-gravatar-block">
	<img decoding="async" src="https://secure.gravatar.com/avatar/aca52b16e75868bc4c6533bdeac750ef?s=96" data-gravatar-hash="aca52b16e75868bc4c6533bdeac750ef" data-gravatar-size="96" alt="Gravatar" width="96" height="96">
</div>Code language: HTML, XML (xml)

In the block’s HTML, the attributes are stored as properties.

<!-- wp:dlxplugins/dlx-gravatar-block {"gravatarHash":"aca52b16e75868bc4c6533bdeac750ef","gravatarSize":"48"} -->
<div class="wp-block-dlxplugins-dlx-gravatar-block dlx-gravatar-block"><img src="https://secure.gravatar.com/avatar/aca52b16e75868bc4c6533bdeac750ef?s=48" data-gravatar-hash="aca52b16e75868bc4c6533bdeac750ef" data-gravatar-size="48" alt="Gravatar" width="48" height="48"/></div>
<!-- /wp:dlxplugins/dlx-gravatar-block -->Code language: HTML, XML (xml)

For a small static block, these attributes are manageable, but as a block grows, the attributes can bloat the HTML. Let’s optimize this static block’s attributes to read the data attributes from the saved image.

Let’s start with gravatarHash.

{
	"attributes": {
		"gravatarHash": {
			"type": "string",
			"source": "attribute",
			"selector": "img",
			"attribute": "data-gravatar-hash",
			"default": ""
		}
	}
}Code language: JSON / JSON with Comments (json)

In this case, I’m going to use selector syntax to match the data attributes. For example, I’ve added a source parameter, which I’ve set to attribute.

The source variable can be a number of values, but in summary:

  • (omitted) – Data is stored in the block’s comment JSON (not parsed from HTML).
  • attribute – Data comes from an HTML element attribute (e.g., src, href, data-*).
  • text – Data comes from plain text inside the HTML (HTML tags are stripped).
  • html – Data comes from raw HTML inside the element (commonly used by RichText).
  • query – Data is stored/retrieved as an array of objects from repeated elements.

Source: Block Attributes.

In my case, the source parameter is being extracted from an image attribute, so I’m using attribute. I’ll use the selector as an img tag that I’m extracting the attribute from.

Finally, there’s the attribute parameter, which is the HTML attribute I’m extracting the data from (data-gravatar-hash).

I can do the same for gravatarSize:

"gravatarSize": {
	"type": "string",
	"source": "attribute",
	"selector": "img",
	"attribute": "data-gravatar-size",
	"default": "96"
}Code language: JSON / JSON with Comments (json)

The result, if a fresh block is inserted and configured, is just clean block HTML.

<!-- wp:dlxplugins/dlx-gravatar-block -->
<div class="wp-block-dlxplugins-dlx-gravatar-block dlx-gravatar-block"><img src="https://secure.gravatar.com/avatar/aca52b16e75868bc4c6533bdeac750ef?s=96" data-gravatar-hash="aca52b16e75868bc4c6533bdeac750ef" data-gravatar-size="96" alt="Gravatar" width="96" height="96"/></div>
<!-- /wp:dlxplugins/dlx-gravatar-block -->Code language: HTML, XML (xml)

You can go a lot crazier with block attributes and their various sources, but these only work well for static blocks. As most blocks are dynamic, I won’t dive further into the topic.

The ‘styles’ property

These allow you to set various styles for your block. For example, if I wanted my Gravatar block to have a rounded and squared appearance, I could add the following to my block.json.

"styles": [
	{
		"name": "rounded",
		"label": "Rounded",
		"isDefault": false
	},
	{
		"name": "square",
		"label": "Square",
		"isDefault": true
	}
]Code language: JSON / JSON with Comments (json)
Block Styles Visible in the Block Editor
Block Styles Visible in the Block Editor

Surrounding my block, if I select Rounded, the class is-style-rounded will be added to my block’s HTML. I can then use CSS to style the different block styles.

Please note that you should avoid changing a block’s behavior with block styles. These are meant to be for appearance only. For changing a block’s behavior, you’re likely after block variations or separate toggles.

The ‘editorScript‘, ‘editorStyle’, ‘style’, and ‘viewScript’ properties

The following properties determine which styles and scripts load for your block.

  • editorScript: file location to your script, or enqueue handle (loads in the Block Editor only)
  • editorStyle: file location to your script, or enqueue handle (loads in the Block Editor only)
  • style: file location to your frontend and block editor styles, or enqueue handle (loads on the frontend/backend)
  • viewScript: any scripts you’d like to load on the frontend (only loads on the frontend)

Here’s an example using filenames:

"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",Code language: JSON / JSON with Comments (json)

This of course assumes everything is relative to my block’s build directory.

Files Relative to My Block's Build Directory
Files Relative to My Block’s Build Directory

If using files or handles, you can pass in an array of values. Here’s one from my plugin AlertsDLX.

"editorScript": "alerts-dlx-block",
"editorStyle": [ "alerts-dlx-block-editor-styles", "alerts-dlx-bootstrap-light-css","alerts-dlx-bootstrap-dark-css", "alerts-dlx-block-editor-styles-lato" ]Code language: JSON / JSON with Comments (json)

In the above example, I’m using script and style handles since I’m using a multi-block plugin. I’ve found that in multi-block plugins that reference the files directly, multiple versions of that file are loaded.

Enqueue Style and Script Example
Enqueue Style and Script Example

When loading scripts via handles, it’s important to use hook enqueue_block_assets and register your scripts/styles using wp_register_script and wp_register_style.

The ‘textdomain’ property

This is the same value as your plugin’s textdomain property. For example, if my plugin’s textdomain is alerts-dlx, then I would use the same thing as my block.json textdomain property.

"textdomain": "alerts-dlx",Code language: JSON / JSON with Comments (json)

This helps your plugin become translatable and any JavaScript strings will show up when translating your plugin on WordPress.org.

The ‘example’ property

The example property is for previews. For example, the Gravatar block I worked on before had no preview, so if I hovered over the block in the block editor, I would receive the block interface as a preview:

Generic Block Preview
Generic Block Preview on Hover

The example property is simply a way to short-circuit a block by overloading the block’s attributes. For example, here are my block’s attributes currently:

"attributes": {
	"gravatarHash": {
		"type": "string",
		"default": ""
	},
	"gravatarSize": {
		"type": "string",
		"default": "96"
	}
},Code language: JSON / JSON with Comments (json)

What I can do is pass an example object with the attributes pre-loaded. For example, if I wanted to show off my Gravatar as the default:

"example": {
	"attributes": {
		"gravatarHash": "aca52b16e75868bc4c6533bdeac750ef",
		"gravatarSize": "96"
	}
},Code language: JSON / JSON with Comments (json)

Now when I hover over the block, it should show my Gravatar.

Block Preview With Avatar Showing
Block Preview With Avatar Showing

For advanced cases where a block preview isn’t feasible, you can pass a preview flag and return an image. I go over this in my article on setting block previews.

The ‘transforms’ property

Block Transforms allow you to change from one block to another. For example, you can quickly switch between an image block and a cover block with just a few clicks.

You can mostly skip transforms in block.json. Transforms should go into your block registration file.

Transforms In Block Registration
Transforms In Block Registration

For more on transforms, I go over block transforms and why you should use them.

The ‘parent’ property

The parent property is useful if you have an innerBlock, or child block, that needs to reference the parent block.

For example, I have a caption block for my photo block, so I reference the parent.

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "title": "Caption Block",
  "apiVersion": 3,
  "name": "dlxplugins/photo-caption-block",
  "parent": [
    "dlxplugins/photo-block"
  ]
}Code language: JSON / JSON with Comments (json)

The parent will always be the parent block’s namespace, or block name.

If you don’t specify the parent, and you register your block as normal, it’ll show up as a regular block in the block inserter.

For more on InnerBlocks, please see my article on creating nested blocks.

The ‘children’ property

For the children property, if you have any child blocks, you can list those here.

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "title": "Photo Block",
  "apiVersion": 2,
  "name": "dlxplugins/photo-block",
  "children": [
    "dlxplugins/photo-caption-block"
  ],
  "category": "media"
}Code language: JSON / JSON with Comments (json)

For example, my “Photo Block” has a child caption block. This ensures that only caption blocks can become a child of my main photo block.

The ‘variations’ property

Block Variations Show Different 'Variations' of a Block
Block Variations Show Different ‘Variations’ of a Block

For block variations, think of these as clone copies of your block, just with different starter attributes. For example, I can create a default “medium” Gravatar block by including this variation:

"variations": [
	{
		"name": "dlxplugins/dlx-gravatar-block-medium",
		"title": "DLX Gravatar Block - Medium",
		"description": "A medium size gravatar",
		"attributes": {
			"gravatarSize": "48"
		},
		"icon": "smiley"
	}
],Code language: JSON / JSON with Comments (json)

Block Variations can get pretty complex, allowing you to set InnerBlocks and such. These are typically registered via JavaScript rather than in block.json. Please see Variations in the Block Editor Handbook for further reading.

The ‘deprecated’ property

The deprecated property, like transforms, shouldn’t go in your block.json file and should instead go in your block registration.

Deprecations allow you to run custom code on properties that are no longer attributes. For example, in my Gravatar Block, if I wanted to deprecate attribute gravatarSize for size.

deprecated: [
	{
		attributes: {
			gravatarSize: { type: 'string', default: '96' }
		},
		save: () => null, // Still null for dynamic
		migrate( attributes ) {
			return {
				size: attributes.gravatarSize
			};
		}
	}
]Code language: JavaScript (javascript)

In this case, I have an old attribute, gravatarSize, which I need to port to a new attribute called size.

Deprecations can be intense, so I recommend you check out the guide on Block Deprecations.

Conclusion

We’ve spent this article digging into the block.json file — the quiet backbone of modern WordPress block development. It’s the one place that ties your block’s identity, behavior, and assets together, making registration simpler, performance better, and your codebase more maintainable. Whether you’re new to blocks or have been building them since the early Gutenberg days, understanding block.json is essential.

We walked through the recommended file structure, how to generate block.json quickly, and what each property does — from apiVersion and attributes to supports and beyond.

I hope you’ve enjoyed this guide. Please share where needed.

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 *