Create Your First Gutenberg Block
In this lesson, I will guide you through the steps you need to take when you create a custom a Gutenberg block completely from scratch (even without using @wordpress/create-block).
However, if you are a big fan of scaffolding starter Gutenberg blocks using that tool, this lesson will still be helpful for you because you will learn how everything is working under the hood.
As a reminder, we will start to create a static “Subscription form” block, this one:

File Structure of a Custom Gutenberg Block
We can begin by creating the following files in the block folder.
| File | Description |
|---|---|
misha-block.php | Main block and plugin file. |
block.json | Contains block information. |
src/index.js | Contains JavaScript (JSX) code of the block. |
src/editor.scss | CSS styles of the block in the Block Editor. |
src/style.scss | CSS styles of the block in both the Block Editor and on the website. |
You can also feel free to create a /build folder where all the compiled files will be generated automatically by the @wordpress/scripts tool. You can also put the block.json file into the /src folder, and after that, its copy will be created in the /build folder automatically as well, but I prefer not to do that.
register_block_type() – Registering Gutenberg Blocks in PHP
Just in case, it is not what you think it is. Of course, we are not going to use PHP for Gutenberg block development; however, we still need to add a couple of lines into the main block/plugin PHP file, which is, in our case, misha-block.php.
All we need to do is use the register_block_type() function inside the init action hook. In this function, you can either provide some block information or just a directory path to the block.json file. Let’s do the latter.
/*
* Plugin name: Block by Misha
* Version: 1.0.0
* Author: Misha Rudrastyh
* Author URI: https://rudrastyh.com
* Plugin URL: https://rudrastyh.com/gutenberg/create-a-block.html
*/
add_action( 'init', 'rudr_register_in_php' );
function rudr_register_in_php() {
register_block_type( __DIR__ );
}In my case, the block.json file is exactly in the same directory as the misha-block.php file is, which means that we can provide __DIR__ as an argument of the register_block_type() function.
In case you decided to go another way and put your block.json file into the /src folder, then you need to specify the path to the /build folder.
register_block_type( __DIR__ . '/build' );block.json
Using the block.json file has only become available since WordPress version 5.8. Before that, we had to put the block information half in the register_block_type() PHP function and half in the registerBlockType() JavaScript function (we will talk about it soon).
I preferred to do as many things on the client side as possible, but I know that most of the developers tried to do the opposite – as many things as possible on the server side.
Below is an example of the block.json configuration file that we are going to use for our “Subscription form” block. Of course, it has many more parameters available, and you can find all of them in the official WordPress documentation. Plus, we will learn some of the parameters we will learn later during this course.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "rudr/subscribe-form",
"title": "Subscription form",
"description": "This block displays a subscription form",
"category": "text",
"icon": "email",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./assets/style.css",
"style": "file:./assets/front.css",
"supports": {
"html": true,
"align" : [ "wide", "full" ]
}
}So far we have:
$schemaandapiVersionare not something you should be concerned about; you can leave them be unless you have some specific requirements.name– It is the block slug with its namespace. The namespace can be a string that you will use for all Gutenberg blocks you develop, for example, I use “rudr”.title– The block name, how it is going to be displayed in the Block Editor.description– The block description, which will be displayed in the Block Editor when you hover over a block in the inserter.category– Here, you can set in which group of blocks in the Inserter your custom block should appear. The standard ones are:media,design,widgets,theme,embedandtext. But it is possible to create a custom category there.icon– I don’t usually use this parameter because it only allows you to choose an icon from the Dashicons set (you just need to provide an icon slug). I usually use custom SVG icons for Gutenberg blocks, so we can skip this parameter and do the stuff in theregisterBlockType()function.editorScript– Most of the block code will be in this JavaScript file.editorStyle– Provide the path to a CSS (or SCSS) file with styles that should be applied only in the Block Editor.style– The path to a CSS (or SCSS) file with styles that will be applied to your custom block in both the Block Editor and on the website.supports– This parameter can have a lot of additional block configuration, and we will learn some of them in the following tutorials. For now, I allow the users to view the block HTML with the help of"html": trueand to make the block wide or full-width.
As a reminder, you can always download the files of our custom Gutenberg block for every lesson using the link below:
registerBlockType() – Registering the Block in JavaScript
Below is the code for the main JavaScript file of the Gutenberg block, which is src/index.js.
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import metadata from './../block.json';
registerBlockType(
metadata,
{
edit: () => {
return(
<div {...useBlockProps()}>
<h3>Subscribe to my newsletter</h3>
<p>
<span>Email address</span>
<span>Subscribe</span>
</p>
</div>
)
},
save: () => {
return (
<div {...useBlockProps.save()}>
<h3>Subscribe to my newsletter</h3>
<form>
<input type="email" placeholder="Email address" />
<button>Subscribe</button>
</form>
</div>
)
}
}
)Now, let’s discuss the above code a little bit:
- As you can see, we don’t provide any block information (a block name, description, etc), because we import it from the
block.jsonfile instead. - The
edit()method generates the block HTML markup, which is used only inside the Block Editor. This is actually a critical point to understand, because, for example, in our case of a subscription form block, it means that we don’t have to use<input>,<form>, or<button>tags inside this method. Why? Because you are not going to submit your form when editing a post, are you? - On the other hand, the
save()method should return the HTML of a block exactly the same way as how the block is going to be displayed on the website pages and saved to the database. That’s where we can use<form>,<input>and<button>HTML tags.
Moving save() and edit() methods into separate files
The code above is completely fine; however, if you’re working on a complicated custom block for Gutenberg, it will be much more convenient for you, I guess, if you do the work with both edit() and save() methods in separate JavaScript files.
In that case, this is how our src/index.js file is going to look:
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import Save from './save';
import metadata from './../block.json';
registerBlockType(
metadata,
{
edit: Edit,
save: Save
}
)src/edit.js file:
import { useBlockProps } from '@wordpress/block-editor';
export default function Edit() {
return(
<div {...useBlockProps()}>
<h3>Subscribe to my newsletter</h3>
<p>
<span>Email address</span>
<span>Subscribe</span>
</p>
</div>
)
}src/save.js file:
import { useBlockProps } from '@wordpress/block-editor';
export default function Save() {
return (
<div {...useBlockProps.save()}>
<h3>Subscribe to my newsletter</h3>
<form action="">
<input type="email" placeholder="Email address" />
<button>Subscribe</button>
</form>
</div>
)
}You can download the Gutenberg block files from this tutorial using the link below:
Misha Rudrastyh
Hey guys and welcome to my website. For more than 15 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Thanks
Great
Great post, thanks!
But why I can’t do it when I create my custom theme? Why I must cheate some plugin for this? How I can create my custom Gutenberg block in my WordPress theme?
Thank you!
You can easily do it in a custom theme, why not
What’s your opinion about putting custom gutenberg block into the theme or as a plugin?
In this blog it’s said that having a plugin is better. https://jasonyingling.me/gutenberg-best-practices-for-blocks-and-themes/
But when developing, I prefer to have everything in one place instead of two.
And how would you add the custom gutenberg block into the theme instead as a plugin?
My opinion is that putting it in themes is ok 🙃
Hi in which file am I supposed to put the registerBlockType function?
Hi Kate,
In
src/index.js.