Now that we’ve covered best practices, enqueueing scripts, and modern JavaScript in Gutenberg block development we’re ready to take a look at the Block API.
Note: Code examples in this post will be using modern JavaScript. For ES5 examples check out the Gutenberg Handbook.
What is the Block API
Blocks are the elements developers can use to extend and create functionality in the WordPress editor within plugins and themes.
The Block API is the functions WordPress exposes for extending the Gutenberg editor with that custom functionality by creating new blocks.
You’ll typically make use of the Block API in the index.js
file which should be the main JavaScript for creating your block.
Defining Functions from Dependencies
At the top of your index.js
file you’ll likely want to define some of the functions you’ll be using from the JavaScript libraries made available in Gutenberg.
// Import CSS.
import './style.scss';
import './editor.scss';
// let's us call registerBlockType() directly from the wp.blocks library
const { registerBlockType } = wp.blocks;
// let's us use the RichText component from the wp.editor library
const { RichText } = wp.editor;
// let's us use components such as a Spinner (loading wheel) from the wp.components library
const { Spinner } = wp.components;
// let's us use the withSelect() function from the wp.data library to access the Rest API
const { withSelect } = wp.data;
//let's us use the __() function from the wp.i18n library to translate strings
const { __ } = wp.i18n;
Doing this allows you to call the functions or components independently. For example, the above let’s us use registerBlockType
rather than wp.blocks.registerBlockType
.
You may have noticed I didn’t explain the two .scss
files being imported at the top of the code. This is a pattern from React that is used in the core Gutenberg development. It allows for scoping styles to particular blocks.
When using webpack to process and spit out our CSS I don’t believe this is necessary (or does anything). I’m still trying to develop a solid answer on this particular item. I’ll update as I learn more and if you can shed light on it let me know in the comments!
Update: The use of these will depend on the way you’re enqueueing your assets and using webpack. If you enqueue each block’s assets individually through PHP you probably don’t need these imports. But if you use Create Guten Block it runs a build process that compiles your SCSS files into a build CSS file that is enqueued on it’s own. Without the import
the SCSS styles wouldn’t be compiled into the build file.
Creating a Block
In order to create a block within our index.js
file we’re going to want to use the registerBlockType
function from the wp.blocks library.
registerBlockType
takes two arguments block-name and a block configuration object.
The block-name is the namespace/block-name
where the namespace is your plugin name and the block-name is, well, your block’s name.
The block configuration object is an object containing the properties and functionality of your block. The magic really happens within the edit
and save
functions within the block configuration object.
But First, A Look at the Properties
The attributes passed to the block configuration object will define the block and the data it will be using.
Defining your Block
A lot of the properties passed to the block configuration object help the end user understand and find your block. Some examples:
title – The title of your block.
description – A short description of the block’s functionality.
icon – A dashicon or a custom SVG used to identify the block.
category – A category to help users browse and discover blocks. Core categories include common, formatting, layout, widgets, and embed.
keywords – Sometimes your block needs to use an alias to be discovered. Think an image block using the keyword “photo”. Three additional keywords can be set per block.
registerBlockType('namespace/block-name', {
title: __('My Block'),
description: __('A Custom Block'),
icon: 'shield',
category: 'common',
keywords: [
__('custom-block'),
__('Custom Block'),
__('Another Keyword')
],
});
Diving into Attributes
Attributes are the secret sauce of block properties. Attributes allow the Gutenberg editor to extract the value from a saved post and make it usable in an editable block.
What does that mean? It’s basically how you define the data your block will be able to edit within the JavaScript representation of the block.
Let’s say we’ve got an image and we want to get the src
for our url
attribute. Our image markup may look like:
<img src=“https://lorempixel.com/1200/800/“ />
First we’ll want to define a url
attribute within our attributes
property.
attributes: {
url: {
}
}
We want to define what type of data we’ll be working with using type
. In the case of our image src
we’re looking for a string.
attributes: {
url: {
type: 'string',
}
}
Next we need to set the source for our attribute. Since src
is a HTML attribute of the <img>
element. We’ll set our attribute
source to 'attribute'
.
attributes: {
url: {
type: 'string',
source: 'attribute',
}
}
The next thing we want to do is identify what element we’re pulling the HTML attribute from. We can do that by setting the selector
as our <img>
element, a class
, or id
.
attributes: {
url: {
type: 'string',
source: 'attribute',
selector: 'img',
}
}
Cool, cool, cool. Now we have to actually identify the HTML attribute we’re pulling into our url
attribute. We do that by setting the attribute
as src
.
attributes: {
url: {
type: 'string',
source: 'attribute',
selector: 'img',
attribute: 'src',
}
}
https://giphy.com/gifs/community-abed-cool-2HONNTJbRhzKE
So that’s one of the more confusing examples because everything uses the term attribute. Just keep in mind url
is one of your block attributes
, source
says we’re looking for an HTML attribute, and attribute
defines the HTML attribute we want.
More Attribute Sources
Attribute is just one of the common sources used for block attributes. Let’s take a look at a few others before we move on.
Text vs HTML
The text attribute let’s you get the inner text from an element. Let’s say we want the text within this <p>
element.
<p>Maecenas faucibus <strong>mollis</strong> interdum.</p>
We can get the text with:
attributes: {
content: {
type: 'string',
source: 'text',
selector: 'p',
}
}
That will give us:
{ "content": "Maecenas faucibus mollis interdum." }
Notice the <strong>
tag is missing. If we want to return the strong tag as well we need to use the HTML source.
attributes: {
content: {
type: 'string',
source: 'html',
selector: 'p',
}
}
This will return:
{ "content": "Maecenas faucibus <strong>mollis</strong> interdum." }
Children
One fo the more useful sources is the children
source. Which returns an array of the child nodes of a matched element.
You’ll notice above the HTML source returned the strong tag, but we’d not able to independently access it.
We can use the children
source on the same <p>
element as above.
attributes: {
content: {
type: 'array',
source: 'children',
selector: 'p',
}
}
And that will return the following:
{
"content": [
"Maecenas faucibus ",
{ "type": "strong", "children": "mollis" },
" interdum.",
]
}
This is super useful when used in combination with the RichText
element.
registerBlockType('namespace/block-name', {
title: __('My Block'),
description: __('A Custom Block'),
icon: 'shield',
category: 'common',
keywords: [
__('custom-block'),
__('Custom Block'),
__('Another Keyword')
],
attributes: {
url: {
source: 'attribute',
selector: 'img',
attribute: 'src'
}
},
});
Editing and Saving Blocks
The last parts of the block configuration object we’ll take a look at in this post are the edit
and save
functions.
The edit function
The edit
function is used to create a block in the context of the editor. It renders the markup used for the block within the editor as well as takes several properties for use in editing.
attributes
This property gives access to all of the previously defined attributes within the block configuration object.
className
This property returns the class name to be used on the wrapper element. Within the save
function the class name is automatically added to the wrapper element. But in the context of the editor the outermost wrapper may not be the main element used. So by surfacing the className
you can specify where it goes.
isSelected
The isSelected
property let’s you determine if the block is currently selected. In the context of the editor this allows you to display a message or a tooltip when the block is selected in the editor.
setAttributes
This allows the block to update individual attributes as defined in the block configuration object.
A Static Example
To it’s most basic a static block that simply outputs a static message would look like this:
edit: function( { attributes, setAttributes, className, isSelected } ) {
return (
<div className={ className }>
<p>Steve Guttenblock</p>
</div>
);
},
In our editor this will give us a <div>
with our main wrapper class name with a <p>
static paragraph inside.
The save function
The save
function defines the final markup output by the block that is then saved into the content and output on the frontend of the site.
attributes
The save
function only receives attributes
in it’s object argument. This allows for attributes defined in the block configuration object to be combined into the final markup of the block.
save: function( { attributes } ) {
return (
<div>
<p>Steve Guttenblock</p>
</div>
);
}
Note: The final markup doesn’t pull in the className
anywhere as it is automatically applied to the outermost wrapper.
Tie it altogether and we’ve got the following registerBlockType
call.
registerBlockType('namespace/block-name', {
title: __('My Block'),
description: __('A Custom Block'),
icon: 'shield',
category: 'common',
keywords: [
__('custom-block'),
__('Custom Block'),
__('Another Keyword')
],
attributes: {
url: {
source: 'attribute',
selector: 'img',
attribute: 'src'
}
},
edit: function( { attributes, setAttributes, className, isSelected } ) {
return (
<div className={ className }>
<p>Steve Guttenblock</p>
</div>
);
},
save: function( { attributes } ) {
return (
<div>
<p>Steve Guttenblock</p>
</div>
);
}
});
Note: While I have the defined url
attribute in this sample it isn’t used for anything other than to illustrate where attributes are defined.
Where to Now
Now with a solid understanding of the Block API we can delve deeper into Gutenberg block development. Notably taking a look at the RichText
component for making editable blocks. And dynamic blocks making use of the REST API and PHP render_callback
.