Gutenberg Block Generator
Scaffold a complete custom block plugin: the plugin file, block.json, the editor script, the render or save, and both stylesheets.
Enable JavaScript to change the fields; the files below are for the defaults.
Create this folder: wp-content/plugins/pricing-table
A block is not one file. The generator that writes block.json gets you the manifest and leaves you to
write the plugin that registers it, the editor script that gives it an edit function, the save or
render.php that decides what lands in the post, and the two stylesheets. This writes all of them, as
one plugin folder you can drop into wp-content/plugins/ and build.
How to use
- Name the block and the namespace. The namespace is the half before the slash in
my-plugin/pricing-table; use your plugin’s name, because two blocks with the same name cannot both register and the second one silently loses. - Choose dynamic or static. This is the one choice that is expensive to reverse, and the difference is explained under Pitfalls.
- Add the attributes the block needs. The first
stringattribute becomes an editable RichText field in the editor, so the block does something the moment you install it. - Turn on the supports you want. Each one adds controls to the block sidebar and costs you nothing.
- Copy each file, or read down the tabs. The folder path above the tabs is where they go.
npm install, thennpm run build.register_block_typereadsbuild/, notsrc/.
Example
A dynamic block called Pricing Table in the acme-blocks namespace, with a heading string and a
columns number, produces seven files:
pricing-table.php the plugin, registering build/ on init
src/block.json apiVersion 3, the attributes, the supports, render: file:./render.php
src/index.js registerBlockType with edit and save
src/render.php the front end, escaping the heading it prints
src/style.css front end and editor
src/editor.css editor only
package.json wp-scripts build and start
block.json carries the attributes with defaults of the right JSON type, which matters: a number
attribute whose default is the string "3" is a type mismatch WordPress will not correct for you.
Pitfalls
- Dynamic and static are not interchangeable. A static block saves its markup into post content. If
you later change what
savereturns, every post that already contains the block shows “this block contains unexpected or invalid content” until you write a deprecation. A dynamic block saves nothing and renders in PHP on every request, so you can change the markup whenever you like, but the content disappears from the post if the plugin is ever deactivated. - The block name must be unique across the whole site, not just your plugin.
my-plugin/cardis one global name. register_block_typepoints atbuild/. Pointing it atsrc/is the most common reason a newly scaffolded block does not appear: the JSX insrc/index.jshas not been compiled yet.- Attribute names are JavaScript identifiers. Spaces and punctuation are stripped here rather than written into a manifest that will not register.
- This does not write sourced attributes. An attribute with a
sourceand aselectorparses its value back out of saved markup, and a selector that does not match the markup you write next is the invalid-content error above. Add those by hand, against markup you have already settled.
Compatibility
apiVersion: 3 needs WordPress 6.3 or newer; the generated plugin declares 6.5 because that is where
render: file:./render.php in block.json is dependable. The build needs Node 18 or newer and
@wordpress/scripts 27, which the generated package.json pins. Nothing here is uploaded: the files
are written in your browser.
Frequently asked questions
Should I pick dynamic or static?
Why is there no save for my dynamic block?
save writes the inner blocks out so render.php receives them as
$content. The generator handles that for you.Can I add my own fields to the block sidebar?
edit function is a normal component. Import InspectorControls from
@wordpress/block-editor and put PanelBody inside it; the attributes are already declared.Does it support InnerBlocks?
useInnerBlocksProps so the block is a
drop target, and a dynamic block still saves its children.Why does the editor show a dashed outline on my block?
src/editor.css, which editorStyle loads in the editor only. Delete the rule if you do not
want it; nothing in that file reaches the front end.