How to Build Custom Gutenberg Blocks: a Beginner’s Guide

In this tutorial we will learn to build custom blocks for the new Gutenberg editor in WordPress. Gutenberg is built using the React Javascript framework and custom blocks can be built with React’s JSX syntax. However, since this is a beginner’s guide and many developers aren’t familiar with JSX or the tools used to compile it, we’re going to work directly in Javascript. This tutorial is broken up into a number of distinct sections. Each section is available as a fully functioning WordPress plugin on GitHub. Here is an overview of the tutorial structure:

Gutenberg block with static content

View static block example plugin on GitHub.

We will be building each example as a fully functioning WordPress plugin, so the first step is to create an installable plugin. In our WordPress plugins directory, we will begin by creating a new folder called gutenberg-block-static-example.

PHP files

Inside this new directory, we will create the main plugin file and save it as gutenberg-block-static-example.php. This file will hold the plugin details used by WordPress to identify the plugin, as well as the code we use to load our Javascript file.

<?php
/**
 * Plugin Name: Gutenberg Block: Static Example
 * Plugin URI: https://github.com/modularwp/gutenberg-example-block-one
 * Description: A example Gutenberg block with static content.
 * Author: ModularWP
 * Author URI: https://modularwp.com/
 * Version: 1.0.0
 * License: GPL2+
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Enqueue the block's assets for the editor.
 *
 * wp-blocks:  The registerBlockType() function to register blocks.
 * wp-element: The wp.element.createElement() function to create elements.
 * wp-i18n:    The __() function for internationalization.
 *
 * @since 1.0.0
 */
function mdlr_static_block_example_backend_enqueue() {
	wp_enqueue_script(
		'mdlr-static-block-example-backend-script', // Unique handle.
		plugins_url( 'block.js', __FILE__ ), // block.js: We register the block here.
		array( 'wp-blocks', 'wp-i18n', 'wp-element' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'block.js' ) // filemtime — Gets file modification time.
	);
}
add_action( 'enqueue_block_editor_assets', 'mdlr_static_block_example_backend_enqueue' );

As you can see, this file begins with the plugin details and then contains a short check to make sure the plugin is being used in its proper context and not accessed directly. Finally, it contains the code to load the Javascript file that will hold the rest of the code for our custom Gutenberg block. For further information on exactly how this Javascript file is loaded, see the wp_enqueue_script() function documentation.

Javascript files

Next we will create a new file called block.js in our gutenberg-block-static-example directory and add the following code:

/**
 * Static Block Example
 *
 * https://github.com/modularwp/gutenberg-block-static-example
 */
( function() {
	var __ = wp.i18n.__; // The __() function for internationalization.
	var createElement = wp.element.createElement; // The wp.element.createElement() function to create elements.
	var registerBlockType = wp.blocks.registerBlockType; // The registerBlockType() function to register blocks.

	/**
	 * Register block
	 *
	 * @param  {string}   name     Block name.
	 * @param  {Object}   settings Block settings.
	 * @return {?WPBlock}          Block itself, if registered successfully,
	 *                             otherwise "undefined".
	 */
	registerBlockType(
		'mdlr/static-block-example', // Block name. Must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
		{
			title: __( 'Static Block Example' ), // Block title. __() function allows for internationalization.
			icon: 'lock', // Block icon from Dashicons. https://developer.wordpress.org/resource/dashicons/.
			category: 'common', // Block category. Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.

			// Defines the block within the editor.
			edit: function( props ) {
				return createElement(
					'p', // Tag type.
					{
						className: props.className,  // Class name is generated using the block's name prefixed with wp-block-, replacing the / namespace separator with a single -.
					},
					'Static block example.' // Block content
				);
			},

			// Defines the saved block.
			save: function( props ) {
				return createElement(
					'p', // Tag type.
					{
						className: props.className,  // Class name is generated using the block's name prefixed with wp-block-, replacing the / namespace separator with a single -.
					},
					'Static block example.' // Block content
				);
			},
		}
	);
})();

As you can see, the first thing we do is define a few variables to give us easier access to some of Gutenberg’s built-in features. Let’s take a closer look.

  • var __ = wp.i18n.__; – We can use this function to allow strings within our plugin to be automatically translated.
  • var createElement = wp.element.createElement; – We can use this function to easily create elements that will be output within the editor as well as in the saved post content.
  • var registerBlockType = wp.blocks.registerBlockType; – This is the function we use to register our custom Gutenberg block.

Next, we register our custom block using registerBlockType(). This function accepts two parameters:

  • Block name – This is a string containing the name of our custom block with a namespace prefix. For example: my-plugin/my-custom-block.
  • Block settings – This is an object that handles everything else about our custom block.

Block settings

The block settings object is where the real work happens. We start by defining a few settings for our custom block. These settings help identify our block within the editor and define where it should be found. Here are the most basic settings:

  • title – The block title will be used to identify our block within the editor.
  • icon – The block icon helps to more easily identify our block.
  • category – The block category is used to group our block together with others based on common traits (common, formatting, layout widgets, embed).

Here’s what our block looks like with these settings when we go to add a new block within the Gutenberg editor:

Add new Gutenberg block user interface

 

As you can see, I’ve been using it quite a bit in the process of building and testing it, so it shows up right at the top of the “Recent” section of the “Insert Block” dialog box. If you’ve just created your block, you’ll probably need to scroll down in the “Blocks” section to see it, our use the “Search for a block” feature to find it.

Editing custom Gutenberg blocks

The edit function defines how our block behaves in the editor. This code is located in the block.js file covered earlier. I’ve added it again here for easier reference.

			edit: function( props ) {
				return createElement(
					'p', // Tag type.
					{
						className: props.className,  // Class name is generated using the block's name prefixed with wp-block-, replacing the / namespace separator with a single -.
					},
					'Static block example.' // Block content
				);
			},

In this example, our edit function returns a single element. We are using the createElement function mentioned earlier to create this element. The createElement function accepts three parameters:

  • Tag type – The type of HTML tag that should be created.
  • Settings – Any settings associated with this element. In this case, just the class name. There are many other settings available, some of which will be addressed later in this tutorial.
  • Content – The content within the element.

Saving custom Gutenberg blocks

Defining how a Gutenberg block is saved is similar to defining how it is edited. Because this is a static block with no content editing, the edit and save functions are exactly the same. In the next section there will be differences between the edit and save functions.

Gutenberg block with editable content

Please note: after this tutorial was published, the Editable component was been renamed to RichText. For more information, see the PR on GitHub.

View editable block example plugin on GitHub.

Once again, this example is a fully functional WordPress plugin. You can clone or download the entire plugin from the GitHub link above, or continue for an explanation of the code.

PHP files

The only differences between the index.php file in this example and the first example are the plugin and function names. For a complete explanation of the code in this file, please take a look at the first example (Gutenberg block with static content).

<?php
/**
 * Plugin Name: Gutenberg Block Editable Example
 * Plugin URI: https://github.com/modularwp/gutenberg-block-editable-example
 * Description: A example Gutenberg block with editable content.
 * Author: ModularWP
 * Author URI: https://modularwp.com/
 * Version: 1.0.0
 * License: GPL2+
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Enqueue the block's assets for the editor.
 *
 * wp-blocks:  The registerBlockType() function to register blocks.
 * wp-element: The wp.element.createElement() function to create elements.
 * wp-i18n:    The __() function for internationalization.
 *
 * @since 1.0.0
 */
function mdlr_editable_block_example_backend_enqueue() {
	wp_enqueue_script(
		'mdlr-editable-block-example-backend-script', // Unique handle.
		plugins_url( 'block.js', __FILE__ ), // Block.js: We register the block here.
		array( 'wp-blocks', 'wp-i18n', 'wp-element' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'block.js' ) // filemtime — Gets file modification time.
	);
}
add_action( 'enqueue_block_editor_assets', 'mdlr_editable_block_example_backend_enqueue' );

Javascript files

Just like our static example, our Javascript will all be contained in the block.js file. There are only a few changes we need to make to our plugin to allow the content of our block to be edited. The entire code of the block.js file is displayed below, but I’ll only be explaining the code specific to making the block editable.

/**
 * Editable Block Example
 *
 * https://github.com/modularwp/gutenberg-block-editable-example
 */
( function() {
	var __                = wp.i18n.__; // The __() function for internationalization.
	var createElement     = wp.element.createElement; // The wp.element.createElement() function to create elements.
	var registerBlockType = wp.blocks.registerBlockType; // The registerBlockType() function to register blocks.
	var Editable          = wp.blocks.Editable; // For creating editable elements.

	/**
	 * Register block
	 *
	 * @param  {string}   name     Block name.
	 * @param  {Object}   settings Block settings.
	 * @return {?WPBlock}          Block itself, if registered successfully,
	 *                             otherwise "undefined".
	 */
	registerBlockType(
		'mdlr/editable-block-example', // Block name. Must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
		{
			title: __( 'Editable Block Example' ), // Block title. __() function allows for internationalization.
			icon: 'unlock', // Block icon from Dashicons. https://developer.wordpress.org/resource/dashicons/.
			category: 'common', // Block category. Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
			attributes: {
				content: {
					type: 'string',
					default: 'Editable block content...',
				},
			},

			// Defines the block within the editor.
			edit: function( props ) {
				var content = props.attributes.content;
				var focus = props.focus;

				function onChangeContent( updatedContent ) {
					props.setAttributes( { content: updatedContent } );
				}

				return createElement(
					Editable,
					{
						tagName: 'p',
						className: props.className,
						value: content,
						onChange: onChangeContent,
						focus: focus,
             			onFocus: props.setFocus
					},
				);
			},

			// Defines the saved block.
			save: function( props ) {
				var content = props.attributes.content;

				return createElement(
					'p',
					{
						className: props.className,
					},
					content
				);
			},
		}
	);
})();

The first addition we need to make compared with the static example is to add the editable component. Here is line from the code above that sets this up:

var Editable          = wp.blocks.Editable; // For creating editable elements.

Block settings

Our static block had only three basic settings before the edit and save functions. To deal with editable content, we need a fourth setting called attributes.

			title: __( 'Editable Block Example' ), // Block title. __() function allows for internationalization.
			icon: 'unlock', // Block icon from Dashicons. https://developer.wordpress.org/resource/dashicons/.
			category: 'common', // Block category. Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
			attributes: {
				content: {
					type: 'string',
					default: 'Editable block content...',
				},
			},

The attributes setting holds all the custom data for our block. In this case, we only have one attribute called content. We’ll set the type of this attribute to “string” and the default text to “Editable block content…” If we were saving other forms of data, we could set the type setting to boolean, number, array, or whatever type of data we needed.

Edit function

Next, we’ll update our edit function to make use of this data.

			edit: function( props ) {
				var content = props.attributes.content;
				var focus = props.focus;

				function onChangeContent( updatedContent ) {
					props.setAttributes( { content: updatedContent } );
				}

				return createElement(
					Editable,
					{
						tagName: 'p',
						className: props.className,
						value: content,
						onChange: onChangeContent,
						focus: focus,
						onFocus: props.setFocus
					},
				);
			},

The first change is to set the content variable based on the content attribute we just created. Next, we set the focus variable so we can track when our editable element has focus. Next we have a new function called onChangeContent which we can use to update our content attribute value. Lastly, we create the element that gets displayed within the editor. In the static block example, we created a p tag directly. However, this time we want the content of our tag to be editable, so we set our element to Editable. This takes advantage of the Editable variable we defined at the beginning of this section. This opens up a few more properties for us to use:

  • tagName – The type of element we want to create. For example: p, div, span, etc.
  • className – This is the automatically generated class name. This was used in the first example as well.
  • value – This is the content that can be edited.
  • onChange – Here is where we can define what happens when the content is edited by the end user. In this case, we call the onChangeContent function we created above.

Save function

It only takes a slight change to the save function to allow for our newly editable content to be saved properly.

			save: function( props ) {
				var content = props.attributes.content;

				return createElement(
					'p',
					{
						className: props.className,
					},
					content
				);
			},

The only changes we need to make from the first example are the setting and using of the content variable. That’s all! We now have an editable Gutenberg block.

Gutenberg block with additional block controls

View block controls example plugin on GitHub.

As with the previous examples, this is a fully functional WordPress plugin. You can clone or download the entire plugin from the GitHub link above, or continue for an explanation of the code.

This section uses the Editable block example as a staring point and adds a text alignment control to the block control area. The PHP file is the same as our previous example, other than the obvious name changes to prevent it from clashing with the previous example plugin.

Javascript files

Here is the entire block.js file. Explanations for all the lines we’ve added since the previous example are detailed below this code block:

/**
 * Editable Block Example
 *
 * https://github.com/modularwp/gutenberg-block-editable-example
 */
( function() {
	var __                = wp.i18n.__; // The __() function for internationalization.
	var createElement     = wp.element.createElement; // The wp.element.createElement() function to create elements.
	var registerBlockType = wp.blocks.registerBlockType; // The registerBlockType() function to register blocks.
	var Editable          = wp.blocks.Editable; // For creating editable elements.
	var BlockControls     = wp.blocks.BlockControls; // For adding control elements.
	var AlignmentToolbar  = wp.blocks.AlignmentToolbar; // For creating the alignment toolbar element within the control elements.

	/**
	 * Register block
	 *
	 * @param  {string}   name     Block name.
	 * @param  {Object}   settings Block settings.
	 * @return {?WPBlock}          Block itself, if registered successfully,
	 *                             otherwise "undefined".
	 */
	registerBlockType(
		'mdlr/block-block-controls-example', // Block name. Must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
		{
			title: __( 'Block Controls Example' ), // Block title. __() function allows for internationalization.
			icon: 'admin-tools', // Block icon from Dashicons. https://developer.wordpress.org/resource/dashicons/.
			category: 'common', // Block category. Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
			attributes: {
				content: {
					type: 'string',
					default: 'Block content can be aligned with toolbar.',
				},
				alignment: {
					type: 'string',
				},
			},

			// Defines the block within the editor.
			edit: function( props ) {
				var content = props.attributes.content;
				var alignment = props.attributes.alignment;
				var focus = props.focus;

				function onChangeContent( updatedContent ) {
					props.setAttributes( { content: updatedContent } );
				}

				function onChangeAlignment( updatedAlignment ) {
					props.setAttributes( { alignment: updatedAlignment } );
				}

				return [
					focus &&
					createElement(
						BlockControls,
						{},
						createElement(
							AlignmentToolbar,
							{
								value: alignment,
								onChange: onChangeAlignment
							}
						)
					),
					createElement(
						Editable,
						{
							tagName: 'p',
							className: props.className,
							style: { textAlign: alignment },
							value: content,
							onChange: onChangeContent,
							focus: focus,
							onFocus: props.setFocus
						},
					)
				];
			},

			// Defines the saved block.
			save: function( props ) {
				var content = props.attributes.content;
				var alignment = props.attributes.alignment;

				return createElement(
					'p',
					{
						className: props.className,
						style: { textAlign: alignment },
					},
					content
				);
			},
		}
	);
})();

In addition to the Editable component we loaded in the previous example, we also need to load the BlockControls and AlignmentToolbar components:

	var BlockControls     = wp.blocks.BlockControls; // For adding control elements.
	var AlignmentToolbar  = wp.blocks.AlignmentToolbar; // For creating the alignment toolbar element within the control elements.

Our block settings are similar, but we’ve added an additional attribute to store the text alignment setting:

			attributes: {
				content: {
					type: 'string',
					default: 'Block content can be aligned with toolbar.',
				},
				alignment: {
					type: 'string',
				},
			},

In the Edit function we’re now loading the additional alignment attribute as well:

				var content = props.attributes.content;
				var alignment = props.attributes.alignment;
				var focus = props.focus;

We’ve also added a function to update the attribute when it changes:

				function onChangeAlignment( updatedAlignment ) {
					props.setAttributes( { alignment: updatedAlignment } );
				}

There are also a few changes to what the Edit function is returning:

				return [
					focus &&
					createElement(
						BlockControls,
						{},
						createElement(
							AlignmentToolbar,
							{
								value: alignment,
								onChange: onChangeAlignment
							}
						)
					),
					createElement(
						Editable,
						{
							tagName: 'p',
							className: props.className,
							style: { textAlign: alignment },
							value: content,
							onChange: onChangeContent,
							focus: focus,
							onFocus: props.setFocus
						},
					)
				];

The biggest change is that before the Editable element, we’re creating a BlockControls element with our new AlignmentToolbar nested inside. We have also added a style setting to the Editable element to apply the alignment changes.

The Save function adds the new alignment property as well:

			save: function( props ) {
				var content = props.attributes.content;
				var alignment = props.attributes.alignment;

				return createElement(
					'p',
					{
						className: props.className,
						style: { textAlign: alignment },
					},
					content
				);
			},

We should now be able to align the text within our editable content area.

Gutenberg block with custom styles

View block styles example plugin on GitHub.

You can download this example as a fully functioning WordPress plugin using the link above. In this section, we’re only going to look at how to style our custom block. How to create custom blocks is covered in the first section of this tutorial.

Javascript files

The only thing we need to be aware of from the block.js file is the name we used to register our block:

	registerBlockType(
		'mdlr/block-styles-example', // Block name. Must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
		{

We need to know this because the class name of our block is automatically generated from the block name. WordPress generates the class name by taking the block name, replacing the “/” with a “-” and then prefixes the class name with wp-block. So if our block name is mdlr/block-styles-example then the auto-generated class name will be wp-block-mdlr-block-styles-example. The rest of block.js file is basically the same as the previous example.

CSS files

To style our block, we need to create two new CSS files. The style.css file will hold our universal block styles. !important may be needed depending on your theme styles.

.wp-block-mdlr-block-styles-example {
	background-color: #333333;
	color: #ffffff !important;
	padding: 1em 2em;
}

The editor.css file will hold styles that will only be applied within the Gutenberg editor:

.wp-block-mdlr-block-styles-example {
	border-left: 15px solid #000000;
}

PHP files

This is where we load our new CSS files. In our previous examples, we have been enqueueing our block.js file from the main plugin file (in this case, it will be the gutenberg-block-styles-example.php file). In this example, we will add our editor.css file there as well.

function mdlr_block_styles_example_backend_enqueue() {
	wp_enqueue_script(
		'mdlr-block-styles-example-backend-script', // Unique handle.
		plugins_url( 'block.js', __FILE__ ), // Block.js: We register the block here.
		array( 'wp-blocks', 'wp-i18n', 'wp-element' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'block.js' ) // filemtime — Gets file modification time.
	);

	wp_enqueue_style(
		'mdlr-block-styles-example-backend-style', // Handle.
		plugins_url( 'editor.css', __FILE__ ), // editor.css: This file styles the block within the Gutenberg editor.
		array( 'wp-edit-blocks' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'editor.css' ) // filemtime — Gets file modification time.
	);
}
add_action( 'enqueue_block_editor_assets', 'mdlr_block_styles_example_backend_enqueue' );

The wp_enqueue_style function loads our styles in the editor. The only dependency is wp-edit-blocks, which holds the WordPress core editor styles for blocks. We want our styles to load after the core styles so we can override the core styles if necessary.

Next we will enqueue the style.css file we created that holds the universal block styles. These styles will be applied on both the frontend and within the Gutenberg editor:

function mdlr_block_styles_example_enqueue() {
	wp_enqueue_style(
		'mdlr-block-styles-example-style', // Handle.
		plugins_url( 'style.css', __FILE__ ), // style.css: This file styles the block on the frontend.
		array( 'wp-blocks' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'style.css' ) // filemtime — Gets file modification time.
	);
}
add_action( 'enqueue_block_assets', 'mdlr_block_styles_example_enqueue' );

This is nearly identical to our previous enqueue, but instead of attaching it to the enqueue_block_editor_assets action hook, we’ve attached it to the enqueue_block_assets hook. This will cause it to be loaded on both the frontend and the backend. Also, you’ll notice that the dependency for our style.css file is wp-blocks instead of wp-edit-blocks. This ensures that we are loading our custom styles after the WordPress core universal block styles.

Applied styles

Now that our styles are defined and enqueued, we should be able to see the results both in the frontend and the backend. Here I have the block saved in the “Hello World” post using the Twenty Seventeen theme:

Gutenberg block with custom styles

And here is the same block in the editor, where the editor-specific styles are also applied. You’ll notice the black border on the left that we defined in the editor.css file:

Gutenberg block with custom editor styles

Gutenberg block with inspector controls

View inspector controls example plugin on GitHub.

You can download this example as a fully functioning WordPress plugin using the link above. In this section, we’re only going to look at how to add an inspector control to our block. We’ll use the custom styles block from the last section as a starting point and a control to toggle whether or not our styles should be applied.

PHP and CSS files

Since styling is not the focus of this section, we can remove the editor.css file and simply load the style.css file on both the frontend and the backend. The wp_enqueue_style call for the backend now looks like this:

function mdlr_block_inspector_controls_example_enqueue() {
	wp_enqueue_style(
		'mdlr-block-inspector-controls-example-style', // Handle.
		plugins_url( 'style.css', __FILE__ ), // style.css: This file styles the block on the frontend.
		array( 'wp-blocks' ), // Dependencies, defined above.
		filemtime( plugin_dir_path( __FILE__ ) . 'style.css' ) // filemtime — Gets file modification time.
	);
}
add_action( 'enqueue_block_assets', 'mdlr_block_inspector_controls_example_enqueue' );

Javascript files

The block.js file has changed a bit for this example. The first thing we’ll need to do is add to our list variables we’re using. The InspectorControls, BlockDescription, and ToggleControl lines are new:

	var __                = wp.i18n.__; // The __() function for internationalization.
	var createElement     = wp.element.createElement; // The wp.element.createElement() function to create elements.
	var registerBlockType = wp.blocks.registerBlockType; // The registerBlockType() function to register blocks.
	var Editable          = wp.blocks.Editable; // For creating editable elements.
	var InspectorControls = wp.blocks.InspectorControls; // For adding block controls.
	var BlockDescription  = wp.blocks.BlockDescription; // For adding descriptions to block settings panels.
	var ToggleControl     = wp.blocks.InspectorControls.ToggleControl; // For adding toggle controls to block settings panels.
  • InspectorControls – Used to create a section for adding new block settings in the control area.
  • BlockDescription – Used to add a description to the block settings control area.
  • ToggleControl – Used for creating the toggle control we’re using in this example.

Next we’ll take a look at our block settings:

			title: __( 'Block Inspector Controls Example' ), // Block title. __() function allows for internationalization.
			icon: 'admin-settings', // Block icon from Dashicons. https://developer.wordpress.org/resource/dashicons/.
			category: 'common', // Block category. Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
			attributes: {
				content: {
					type: 'string',
					default: 'Block with styles that can be toggled with an inspector control.',
				},
				applyStyles: {
					type: 'string',
					default: '',
				},
			},

The title and icon have been changed, and in the attributes section we have a new variable called applyStyles, which will hold the CSS class we will use to apply the block styles. Now we’ll move on to the edit function where the biggest changes have occurred.

			// Defines the block within the editor.
			edit: function( {attributes, setAttributes, focus, className} ) {
				const {
					content,
					applyStyles,
				} = attributes;

				function onChangeContent( updatedContent ) {
					setAttributes( { content: updatedContent } );
				}

				function onChangeStyleSettings() {
					if ( applyStyles ) {
						setAttributes( { applyStyles: '' } );
					} else {
						setAttributes( { applyStyles: 'styled' } );
					}
				}

				const controls = focus && [
					createElement(
						InspectorControls,
						{},
						createElement(
							BlockDescription,
							{},
							createElement(
								'p',
								{},
								__('This is an example of a Gutenberg block using inspector controls.')
							)
						),
						createElement(
							ToggleControl,
							{
								label: __('Apply Styles'),
								checked: !!applyStyles,
								onChange: onChangeStyleSettings
							}
						),
					),
				];

				return [controls,
					createElement(
						Editable,
						{
							tagName: 'p',
							className: className + ' ' + applyStyles,
							value: content,
							onChange: onChangeContent
						},
					),
				];
			},

To more easily explain the code above, I’ll be highlighting individual lines below.

The first thing to notice is that we’ve changed the way we’re passing properties into the edit function. Rather than a single variable, we’re grabbing the properties as individual variables and extracting the individual attributes from the attributes section we covered above:

			edit: function( {attributes, setAttributes, focus, className} ) {
				const {
					content,
					applyStyles,
				} = attributes;

We also added a new function named onChangeStylesSettings. This toggles the applyStyles attribute we added to the block attributes. This attribute is toggled between an empty string (no additional classes will be added to the block) and “styled” (a class of “styled” will be added to the block):

				function onChangeStyleSettings() {
					if ( applyStyles ) {
						setAttributes( { applyStyles: '' } );
					} else {
						setAttributes( { applyStyles: 'styled' } );
					}
				}

Now we’ve finally come to the section of code that defines our inspector controls. The following line makes sure that our controls are only visible when this block has the focus:

const controls = focus && [

Next we create an InspectorControls element with a BlockDescription element and a ToggleControl nested inside. Notice on the ToggleControl section the checked parameter is set with a double exclamation point:

checked: !!applyStyles,

This ensures that our check mark is getting a boolean value rather than a string. When this control changes, we’re calling the onChangeStyleSettings function we created earlier.

onChange: onChangeStyleSettings

To wrap up our edit function, we return our new controls along with the editable element we used in previous sections:

				return [controls,
					createElement(
						Editable,
						{
							tagName: 'p',
							className: className + ' ' + applyStyles,
							value: content,
							onChange: onChangeContent
						},
					),
				];

The only things to note about this code are:

  1. In the first line, we’re returning controls instead of just creating an element like we did previously.
  2. For the class name, we’re adding the class name that we’re storing to the block attributes.

Our save function looks similar to our edit function, but much simpler:

			// Defines the saved block.
			save: function( {attributes} ) {
				const {
					content,
					applyStyles,
				} = attributes;

				return createElement(
					'p',
					{
						className: applyStyles,
					},
					content
				);
			},

The only meaningful changes are the new syntax for working with attributes and applying the CSS class. That’s all for this example of adding an inspector control to a custom block.

More to come…

Thanks for following along! I’ll be adding to this tutorial regularly. Feel free to check back often or follow me on twitter for updates. You can also use twitter to let me know if you have any questions or if there’s anything you’d like to see me address in this tutorial.

I want to give a special thanks to Ahmad Awais for his Gutenberg boilerplate. It was extremely helpful to me as I was trying to learn how to work with Gutenberg.