block.json Generator
Generate a block.json for a Gutenberg block: name, attributes, supports, asset file references and the API version the block editor expects.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-plugin/card",
"version": "1.0.0",
"title": "Card",
"category": "design",
"icon": "index-card",
"description": "A titled box with an image and a link.",
"keywords": [
"card",
"box"
],
"attributes": {
"heading": {
"type": "string",
"default": ""
},
"showImage": {
"type": "boolean",
"default": true
}
},
"supports": {
"html": false,
"anchor": true,
"align": [
"wide",
"full"
],
"color": {
"background": true,
"text": true,
"link": false
},
"spacing": {
"padding": true,
"margin": false
},
"typography": {
"fontSize": true,
"lineHeight": false
}
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"render": "file:./render.php",
"textdomain": "my-plugin"
}
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Describe the block and copy out a block.json that the editor, register_block_type() and wp-scripts all read from the same file.
How to use
- Namespace the block with your plugin or theme slug. The full name is stored in post content, so it has to be unique and it has to stay the same.
- Declare every attribute you plan to save. Anything not listed is discarded when the post is saved, which is the usual cause of “my setting does not stick”.
- Choose dynamic rendering when the output depends on anything outside the post: a query, an option, the current user. Static is for markup that never changes after saving.
- Turn supports on deliberately. Every one you enable is another control in the sidebar and another set of classes in the markup.
- Put the file in your block’s folder and point
register_block_type()at that folder, not at the file.
Example
A dynamic block with two attributes:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "acme/card",
"title": "Card",
"category": "design",
"attributes": {
"heading": {
"type": "string",
"default": ""
}
},
"editorScript": "file:./index.js",
"render": "file:./render.php",
"textdomain": "acme"
}
apiVersion 3 is what puts the block editor’s canvas in an iframe. A block written for version 1 or 2 that assumes it shares a document with the editor breaks there.
Pitfalls
- The block name is written into every post that uses the block. Renaming it turns existing content into an invalid block warning.
- An attribute that is not declared is not saved. The editor keeps it in memory until the page reloads, which makes the bug look intermittent.
file:paths are relative to the block folder and are resolved byregister_block_type(). An absolute URL there is not handled.apiVersion3 renders the editor canvas in an iframe. Code that reaches fordocumentor for a global stylesheet stops working.alignwithwideorfulldoes nothing unless the theme supports those alignments.- A dynamic block ignores the saved markup entirely, so a
savefunction that returns anything but null fights the PHP render. viewScriptModuleneeds WordPress 6.5. On older versions useviewScript.- Attribute defaults are not applied to blocks that already exist in content. Old blocks come back with the attribute missing, not with the default.
Compatibility
block.json metadata has been read by register_block_type() since WordPress 5.8, apiVersion 3 needs WordPress 6.3, and viewScriptModule needs 6.5. The $schema line is for your editor’s autocomplete and is ignored by WordPress. The tool runs entirely in your browser.
Frequently asked questions
Do I need apiVersion 3?
Why is my attribute not saved?
attributes. Undeclared values are dropped on save.Dynamic or static rendering?
Where do the file: paths point?
wp-scripts build writes. Point register_block_type() at the folder holding this file.Can I add my own metadata?
WP_Block_Type_Registry. Namespace them so they do not collide later.