Gutenberg Block Tutorial with registerBlockType() and NO Webpack, ES6, JSX or Babel
Since the new Gutenberg block editor has been merged into core with the release of WordPress 5.0, the demand for creating your own blocks has increased greatly. In this short guide we’ll be getting you up and running creating your first block.
Although build tools like webpack are commonly being used, in this tutorial we will show you how to get up and running without webpack, ES6, JSX, Babel or any other heavy development tools.
Building a Block with RichText
and InpsectorControls
The goal of this article is to create a simple callout block. Our block will use a RichText
component and make two settings available under the InspectorControls
in the sidebar. These settings will allow the user to change the background color and text color of the block.
Setting Up the PHP, JS and CSS Files
Our plugin consists of three files. The callout-block.php
to create our plugin and load the resources for our block. The callout-block.js
to create our block and configure everything that it does. And a callout-block.css
file containing all the associated styles for our block.
+—— callout-block
| +—— callout-block.php
| +—— callout-block.js
| +—— callout-block.css
Creating our Plugin and Registering our Block in PHP with register_block_type()
First we need to create our plugin. To do this we add a multi-line-comment to the very beginning of our main callout-block.php
file.
<?php
/**
* Plugin Name: Callout Block
* Description: Creates a Gutenberg Block to add a callout component to the page.
* Author: zgordon, fabiankaegy
* Author URI: https://javascriptforwp.com
* Text Domain: jsforwp
* Version: 1.0.0
*/
From there we want to load our block and it’s styles. To achieve that we need to register both the callout-block.js
and callout-block.css
files using wp_register_script
and wp_register_style
.
Once they are registered we can register the block using the register_block_type
function. We will go over how to do all of this in the next couple of sections.
Registering our JavaScript with wp_register_script
With wp_register_script
we are registering a JavaScript file under a unique handle, telling it where the file is located and adding any dependencies.
Additionally we can pass a version of the script and whether or not to load in the footer.
It is import to note that
wp_register_script
does not load the JavaScript file anywhere. It just gets made available under the handle to be enqueued somewhere else.
// creating a variable for our js file path
$block_path = '/callout-block.js';
// registering the editor script that contains our blocks
wp_register_script(
'jsforwp-callout-block',
plugins_url( $block_path , __FILE__ ),
[ 'wp-i18n', 'wp-element', 'wp-blocks', 'wp-components', 'wp-editor' ],
filemtime( plugin_dir_path( $block_path , __FILE__ ) )
);
In Gutenberg block development, dependencies are fundamental.
Out of the box, WordPress offers a number of packages which we can execute in our block. However, for us to use them, they need to be loaded before our JavaScript gets loads.
In this example we are calling the following dependencies:
- wp-i18n – Used for Internationalization
- wp-element – React & ReactDOM
- wp-blocks – Includes registerBlockTypes
- wp-components – WP UI Component Library
- wp-editor – Contains the RichText editor component
If we do not include the required dependencies then our JavaScript might load after the necessary packages and won’t work.
Here is a list containing all of the available packages regarding Gutenberg Gutenberg Handbook.
Registering our CSS using wp_register_style
wp_register_style
is very similar to wp_register_script
. The only difference is the last property you can pass to the function. Where in the wp_register_script
we were able to tell it to load in the footer, we can now tell it the CSS-Media-Type the resource should be loaded in.
// creating a variable for our css file path
$style_path = '/callout-block.css';
wp_register_style(
'jsforwp-callout-block-styles',
plugins_url( $style_path , __FILE__ ),
[],
filemtime( plugin_dir_path( $style_path , __FILE__ ) )
);
Notice that we do not need to include any CSS dependencies for our block CSS file.
Registering our block using register_block_type
Now that we have registered all our block assets we can now register the actual block with register_block_type
. Behind the scenes do the actual enqueuing of our JavaScript and CSS.
Here is what register_block_type
looks like:
register_block_type( $name:String, $args:Array );
Here is what this will look like completed for our example:
register_block_type( 'jsforwp/callout-block', array(
'editor_script' => 'jsforwp-callout-block',
'style' => 'jsforwp-callout-block-styles',
) );
The first parameter we pass to the function is the name of our block. This will usually consist of the textdomain/block-name
.
For reference, the textdomain in WordPress is very important for translations. Here is a link to the official documentation: I18N Handbook – Text Domains
* Note: A block name can only contain lowercase alphanumeric characters and dashes, and must begin with a letter. It also needs to match up with the name we use inside our registerBlockType() function in JavaScript when we set up our block later on.
The second parameter is there to pass additional arguments inside an array.
To tell WordPress what JavaScript and CSS file to load with the block, we need to add the two handles we registered prior to the array we are passing into as the second parameter. The JavaScript file that contains our block needs to have the key of editor_script
, and the CSS file to load everywhere our Block is used needs the key style
. Additional things you can use are:
editor_style
– Enqueues a css file inside the editorrender_callback
– Only used for dynamic blocks (php rendering of the block)
Putting Things Together
To use all of this in our plugin we can just create a function and hook it into the init
action.
add_action('init', 'jsforwp_register_block_assets');
function jsforwp_register_block_assets() {
// creating a variable for our js file path
$block_path = '/callout-block.js';
// registering the editor script that contains our blocks
wp_register_script(
'jsforwp-callout-block',
plugins_url( $block_path , __FILE__ ),
[ 'wp-i18n', 'wp-element', 'wp-blocks', 'wp-components', 'wp-editor' ],
filemtime( plugin_dir_path( $block_path , __FILE__ ) )
);
// creating a variable for our css file path
$style_path = '/callout-block.css';
wp_register_style(
'jsforwp-callout-block-styles',
plugins_url( $style_path , __FILE__ ),
[],
filemtime( plugin_dir_path( $style_path , __FILE__ ) )
);
// registering our block and passing it the hande of our editor script and our style files.
register_block_type( 'jsforwp/callout-block', array(
'editor_script' => 'jsforwp-callout-block',
'style' => 'jsforwp-callout-block-styles',
) );
}
Here we can see the registering of our JS and CSS as well as our registering of our blocks.
This will let WordPress know our blocks are available, but in order for them to show up in the editor we need to register our blocks with JavaScript as well.
Creating our Block in JavaScript using registerBlockType()
Because of the dependencies we added to our script when we registered it in PHP, we will now have access to them via the global wp
object that WordPress adds to the window
object.
For example, registerBlockType
is part of the wp-blocks
library and therefore available under wp.blocks.registerBlockType
.
wp.blocks.registerBlockType( name:String, typeDefinition:Object );
The registerBlockType
function takes two arguments:
- name: a String containing the block name following the naming convention
"uniquenamespace/block-name"
- typeDefinition: an Object containing the block configuration
In order for a block to be registered successfully, it requires a few properties. These are set inside a configuration object passed as the second property to the registerBlockType
function.
The available properties inside the block settings are:
- title:
String
[*required] - description:
String
- category:
String
[*required] - icon:
string | WPElement | Function | Object
- keywords:
Array
- styles:
Array
- attributes:
Object
- transforms:
Array
- parent:
String
- supports:
Object
- edit:
WPElement
[*required] - save:
WPElement
[*required]
For this guide, we are keeping our block settings as simple as possible. However, If you want to find out more about what every one of these does, you should check out our Gutenberg Block Development Course.
How Do Blocks Work?
Blocks consist of two things, the editing experience and what gets saved into the post_content in the database.
The way these two parts communicate, and share data, is by using attributes. Attributes are an abstraction layer WordPress build on top of React’s state. Think of a React’s state as an object containing all the data our block needs. All the properties of this object are stored in either in the HTML comments above the block or inside the actual markup.
WordPress will pass an object into our edit
and save
functions containing our attributes and some other useful things like a function to update attribute values.
edit: function(props) {
...
}
Inside of our edit function we can access our attributes like this: props.attributes.attributeName
.
If we want to set the value of an attribute we can use the function setAttributes
that gets passed as one of the functions attached to our props.
Some of the other things we get passed via the props
are:
- setAttributes:
Function
– Used for updating a attribute value - isSelected:
Boolean
– Tells whether the block is currently selected - className:
String
– The name that will be assigned to the block on the frontend
The setAttributes
function takes one object as an argument. This object should contain the name of the attribute we want to change and the new value for that attribute. So if we wanted to update our backgroundColor attribute, it would look like this:
attributes: {
backgroundColor: {
value: "#442283",
type: "string"
}
},
edit: function(props) {
props.setAttributes({
backgroundColor: "#39ab00"
})
}
This setup will allow us to update our attribute values in our edit function and then later get those values in our save function for saving as part of the_content.
Setting Up the Skeleton of our Block
To start actually writing our block we first need to pass our block name to the registerBlockType
function. In addition to the name we also need to pass it our options object as the second parameter, which takes the following properties:
title
The title will be used by WordPress every time the block is displayed anywhere in the editor.
icon
We also want our block to have an icon. There are a few options we could use here, but to keep it simple we just select one from the collection of WordPress Dashicons, and set icon
to the name of the selected icon (Without dashicon-
at the beginning of the name). It is also possible to use SVG icons.
category
The category we use will decide where in the block selection panel our block will be displayed. The options provided by WordPress are:
- common
- formatting
- layout
- widgets
- embed
It is also possible to create custom categories down the road if you like as well.
attributes
As far as attributes come, we basically have three things we need to keep track of in our block: our actual content, the text color, and background color. All our attributes are properties we set in our attributes object. each of these properties then has an object as its value. And in there you can specify a couple of things.
- type: The type of data stored in the attribute (ie
string
,boolean
,array
, etc) - source: Where the data is coming from (
html
ormeta
for example). If left empty the attribute value will be saved in a comment. - selector: CSS selector to tell it where in the block to get the data from (only needed if source is
html
) - meta: Name of the meta value you want to work with. Only needed if you want to save the block value as a post meta value
- default: A default value (optional)
We only need to specify the type
. However, the more concise we get with our attributes, the better we can control what actually happens.
Here is what registerBlockType()
will look like once we set it up with our attributes.
wp.blocks.registerBlockType( 'jsforwp/callout-block', {
title: 'Callout Block',
icon: 'megaphone',
category: 'common',
attributes: {
content: {
type: 'string',
source: 'html',
selector: 'h2',
},
backgroundColor: {
type: 'string',
default: '#900900',
},
textColor: {
type: 'string',
default: '#ffffff',
}
}
...
}
Handling the Editing Experience with edit
With our skeleton setup, we can actually start configuring the editing experience our block will provide the user with.
WordPress provides many components to us that we can just use in our code. There is a great list of all the available components available at this link: WP-Storybook. But we can also look at the Gutenberg Handbook for a list of all of them.
One important thing to note about our
edit
orsave
functions: we can only return one element. Meaning that if we want to have several components we need to wrap all of them inside one element.
Luckily we don’t just need to put unnecessary elements all over our markup. There is something called a Fragment
that is also part of the wp-element
package. A Fragment
is a React component that won’t produce any markup but counts as a valid wrapper element.
The createElement
function
To use one of these components we can use the createElement
function, which is part of the wp-element
package.
The
wp-element
package is basically React bundled inside of WordPress. That means that thewp.element.createElement
function is the same asReact.createElement
from React.
The createElement
function takes the type of element we want to create, an object containing all of the properties or settings we want to pass it and all children (either text or other React elements) after that.
wp.element.createElement( type, props, children... )
Editing Our Block Content with <RichText />
For the main element of our block we’ll be using the RichText Component, which can be found inside the wp-editor
package. This component takes a couple of arguments, that we can pass in via the second parameter of the createElement function. In this case, we want our content to be displayed as an h2 element. Therefore we can pass it the tagName property with the value of h2. Other things we can set are:
- className
- value
- style
- onChange
- … and many more
A good place to find out what arguments a component takes is the Gutenberg Handbook.
Here is what RichText
looks like when we configure it.
edit: function(props) {
return wp.element.createElement(
wp.editor.RichText,
{
tagName: 'h2',
className: props.className,
value: props.attributes.content,
style: {
backgroundColor: props.attributes.backgroundColor,
color: props.attributes.textColor
},
onChange: function( newContent ) {
props.setAttributes( { content: newContent } );
}
}
);
}
Handling What Gets Saved as Content with the save
Function
Now that we have our basic editing experience setup, it’s time to handle what actually gets saved into the post_content. This means, that the elements we return from inside the save function will be converted into html that is then saved in the database.
In the example below we will use RichText.Content
to display the h2 and set the styles.
save: function(props) {
return wp.element.createElement(
wp.editor.RichText.Content,
{
tagName: 'h2',
value: props.attributes.content,
style: {
backgroundColor: props.attributes.backgroundColor,
color: props.attributes.textColor
},
}
);
}
At this point, our block should work. If we activate the plugin and navigate to a post or page, our block should show up in the inserter.
If we type some text inside the block and save the page, it should show up on the front end of our site.
However, there was some more we set out to do. We also want to create two color pickers inside the block sidebar.
Adding A Block Sidebar with <InspectorControls />
for Block Settings
We can add a sidebar to control the settings of our block with the InspectorControls
element WordPress provides us with as a part of the wp-editor
package. And the setting we want to add is called PanelColorSettings
and is also part of that package.
But like I mentioned earlier we can’t just add the InspectorControls
next to our RichText
element. In order to have them on the same level, we need to wrap them in a Fragment
element.
edit: function( props ) {
return wp.element.createElement(
wp.element.Fragment,
null,
wp.element.createElement(
wp.editor.InspectorControls,
null,
null
),
wp.element.createElement(
wp.editor.RichText,
{
tagName: 'h2',
className: props.className,
value: props.attributes.content,
style: {
backgroundColor: props.attributes.backgroundColor,
color: props.attributes.textColor
},
onChange: function( newContent ) {
props.setAttributes( { content: newContent } );
}
}
)
)
}
From there we can add our PanelColorSettings
as one child element of our InspectorControls
. This component gives us an automatic color picker.
wp.element.createElement(
wp.editor.InspectorControls,
null,
wp.element.createElement(
wp.editor.PanelColorSettings,
{
...
}
)
),
The props we can pass to the PanelColorSettings
are a title and an array containing color settings. Each color setting should be an object with the properties label, value, and onChange:
{
label: wp.i18n.__("Text Color", "jsforwp"),
value: props.attributes.textColor,
onChange: function( newColor ) {
props.setAttributes({ textColor: newColor });
}
}
The onChange
property is where we will place our call to update the textColor
attribute when a new color is selected.
Our Final JavaScript Block Code
Once we put all of this together, our final callout-block.js
will look like this:
wp.blocks.registerBlockType( 'jsforwp/callout-block', {
title: 'Callout Block',
icon: 'megaphone',
category: 'common',
attributes: {
content: {
source: 'html',
selector: 'h2',
},
backgroundColor: {
type: 'string',
default: '#900900',
},
textColor: {
type: 'string',
default: '#ffffff',
}
},
edit: function( props ) {
return wp.element.createElement(
wp.element.Fragment,
null,
wp.element.createElement(
wp.editor.InspectorControls,
null,
wp.element.createElement(
wp.editor.PanelColorSettings, {
title: wp.i18n.__("Color Settings", "jsforwp"),
colorSettings: [
{
label: wp.i18n.__("Background Color", "jsforwp"),
value: props.attributes.backgroundColor,
onChange: function( newBackgroundColor ) {
props.setAttributes({ backgroundColor: newBackgroundColor });
}
},
{
label: wp.i18n.__("Text Color", "jsforwp"),
value: props.attributes.textColor,
onChange: function( newColor ) {
props.setAttributes({ textColor: newColor });
}
}
]
}
)
),
wp.element.createElement(
wp.editor.RichText, {
tagName: 'h2',
className: props.className,
value: props.attributes.content,
style: {
backgroundColor: props.attributes.backgroundColor,
color: props.attributes.textColor
},
onChange: function( newContent ) {
props.setAttributes( { content: newContent } );
}
}
)
);
},
save: function( props ) {
return wp.element.createElement( wp.editor.RichText.Content, {
tagName: 'h2',
value: props.attributes.content,
style: {
backgroundColor: props.attributes.backgroundColor,
color: props.attributes.textColor
},
} );
}
} );
Adding Some Styling to Our Block
At the very beginning of this project, we created a callout-block.css
file and registered it. This file gets loaded everywhere the block is used. The only thing we need to know before we can go ahead styling our block is how we can actually get it.
WordPress takes the name we pass our registerBlockType
function and creates a class from it. The class will follow this naming convention: wp-block-namespace-block-name
. So for our callout block the class will be named wp-block-jsforwp-callout-block
.
With that in mind, we can go ahead and add whatever styling we want to our block.
In this example, we only added some padding to the block, but you could add anything else you need.
.wp-block-jsforwp-callout-block {
padding: 20px;
}
Download the Finished Block
If you would like to grab the completed code to use for your own projects, you can get it from this repo: WordPress Block with NO Webpack Repo.
Taking Things Further
We’ve made it! We successfully created a Gutenberg block together. Of course, there is much more we can explore from here. The next step would be to introduce a build-system to our plugin, which would allow us to use modern JavaScript and JSX in our blocks. We’ll be exploring this and much more in upcoming posts.
If you want to get everything you need in order to get started building your own blocks you should check out the Gutenberg Block Development Course.
In it, you will learn everything from how to set up your project to how to use many of the core components like InspectorControls
and more.
Great tutorial!
Being a PHP programmer i always got lost while trying to create GB blocks based on other tuts because those always lack some crucial info…
Your tut made me really understand how to create a working GB block!
However i am still having difficulties with internationalization of the block. I followed the GB handbook in this regard but it does not work despite i generated the json file. Also tried other ways found on the net but they also do not work… Can you please explain also that part?
Hi Peter,
Awesome! I glad it helped you and was clear to follow.
I myself struggle with the internationalization. One think that I found is that you have to be very careful with the slug and naming of your plugin. All of them need to match exactly in order for it to work. And you need to load the textdomain. https://github.com/fabiankaegy/callout-block/tree/translation
I have a repo where I tried to do it for the Callout while while not using wp-scripts. So maybe you can find something in there 🙂
If you have your code in a repo I’m also happy to take a look at it. You can eiter post the public Link, or add @fabiankaegy to the project and I will take a look at it 🙂
WP-cli had an issue before that was causing me problems. Today i tried V2.2 and the issue is fixed. At last i can i18n also GB blocks. Now it rocks 🙂
I think i will stick with the ancient method (ES5 and no webpack) as i fear i18n may still have issues with JSX and/or minified .js files
Hi Fabian, thanks for the great Tutorial. I’ve read many many tutorials but this is one of the best.
Is the part with the Fragment still valid?
I’ve build a custom block with the inspector too but didn’t use the Fragment. Works fine for me.
https://github.com/leph83/dynamic_gutenberg_block/blob/master/block.js
Hi Phuc,
I’m so glad you enjoyed the tutorial.
Yes awesome, the option you used, is returning an Array of elements. I had to check it out myself and found this Article very useful 🙂 https://pawelgrzybek.com/return-multiple-elements-from-a-component-with-react-16/
So there seem to still be some benefits of using a wrapping element, even if its a Fragment 🙂