Custom Gutenberg Blocks in 10 Steps

Are you looking for a tutorial to get started building custom Gutenberg blocks? Block development can seem daunting at first for someone used to building more traditional WordPress plugins. Follow along with this tutorial series as we look at a simple block creation process to show custom content. We’ll begin by creating a plugin to hold several blocks and create a simple block with a “Hello world” output.

Custom Gutenberg Blocks

Tip: The best way to build custom Gutenberg blocks is with an IDE like Visual Studio or PHPStorm. An IDE allows you to quickly navigate your projects, and many offer built-in terminals.

Custom Gutenberg Blocks Overview

  1. Installing prerequisite software (Node).
  2. Create a parent plugin folder.
  3. Make a parent plugin index file.
  4. Create a hello world plugin folder.
  5. Make a hello world index file.
  6. Create package.json file with npm init.
  7. Add and configure package dependencies with npm.
  8. Create the index.asset.php file with npm build.
  9. Add the Javascript code for rendering your block in Gutenberg
  10. Build the final production block with npm build.

Installing Node and NPM

Before we get started with our Gutenberg tutorial, we’ll need to make sure we have Node, and NPM (Node Package Manager) installed. If you’re using macOS, you can do this easily with Homebrew. Don’t have Homebrew installed? No problem! Check out my tutorial to get your system properly configured! If you’re a Windows user, you can use the Node installer or Chocolatey package manager to quickly get Node on your machine.

Setting Up Our Custom Gutenberg Blocks Plugin

Now that we have Node installed and configured, it’s time to get started with our block setup! Open up a terminal and navigate to your WordPress plugins folder. Once inside the folder, run the following command to create your plugin folder and go inside:

mkdir my-blocks
cd my-blocksCode language: Bash (bash)

Creating Our Plugin Index File

Every WordPress plugin needs an index file. The index file provides information for the administrator section about your plugin, as well as any functions needed to process information or display data. Go ahead and create a new file in your plugin folder titled index.php. Copy the information below into that file and update the fields for author and plugin URI.


 * Plugin Name: My Gutenberg Blocks
 * Plugin URI:
 * Description: A collection of handy Gutenberg blocks for content creation.
 * Version: 1.0.0
 * Author: Your Name
 * @package my-blocks

defined( 'ABSPATH' ) || exit;

include 'hello-world/index.php';
Code language: HTML, XML (xml)

In the code above we set up our default plugin options, as well as restricted direct access to our index file with defined( 'ABSPATH' ) || exit;. This ensures the code is only called within WordPress. The include call is for a folder/file we have yet to create.

Creating Our First Block

Now that we have our parent plugin configured, we can get started creating our first block! Return to your terminal and ensure you’re in the my-blocks directory. If you aren’t sure what directory your terminal is in type pwd. Once you’ve verified your location, go ahead and create your block folder and its index file with the following commands:

mkdir hello-world
cd hello-world
touch index.php
mkdir languagesCode language: Bash (bash)

Configuring Our Block Index File

Now that we have our empty index file, we’re ready to add some code. First, we’ll add some information for WordPress to use the plugin. Change the URI and author to your website and name. We’ll add defined( 'ABSPATH' ) || exit; again to keep our security level high and discourage unauthorized file access.

 * Plugin Name: My Gutenberg Blocks Hello World
 * Plugin URI:
 * Description: This is a simple hello world block for the Gutenberg editor.
 * Version: 1.0.0
 * Author: Your Name
 * @package my-blocks

defined( 'ABSPATH' ) || exit;
Code language: HTML, XML (xml)

Next, we will set up a function to load any language files used for the plugin. For our simple application, this may seem like overkill, but it’s a great pattern to include with all of your plugins and blocks for future extensibility.

 * Load all translations for our plugin from the MO file.
add_action( 'init', 'my_blocks_hello_world_load_textdomain' );

function my_blocks_hello_world_load_textdomain () {
    load_plugin_textdomain( 'my-blocks', false, basename( __DIR__ ) . '/languages' );

}Code language: PHP (php)

Finally, we’re ready to register our block in WordPress. This is the last bit of PHP we’ll be writing for a while. The function will allow our assets to be enqueued and used by Gutenberg.

function my_blocks_hello_world_register_block()
    // automatically load dependencies and version
    $asset_file = include(plugin_dir_path(__FILE__) . 'build/index.asset.php');

    /* Register our script we will be using to develop the plugin. Most ouf our code will be going in here! */
        plugins_url('build/index.js', __FILE__),

    /* Register a gutenberg block with the script we used above */
    register_block_type( 'my-blocks/hello-world', array(
        'editor_script' => 'my-blocks-hello-world',
    ) );

    if ( function_exists( 'wp_set_script_translations' ) ) {
         * May be extended to wp_set_script_translations( 'my-handle', 'my-domain',
         * plugin_dir_path( MY_PLUGIN ) . 'languages' ) ). For details see
        wp_set_script_translations( 'my-blocks-hello-world', 'my-blocks' );

add_action( 'init', 'my_blocks_hello_world_register_block' );Code language: PHP (php)

Configure JSON Package File with NPM

We’re ready to move ahead using NPM to set up our package file! When you run npm init, a wizard will greet you to help configure your package.json file. Use the following options when prompted.

package name: (hello-world) hello-world
version: (1.0.0)
description: A simple hello world static Gutenberg block.
entry point: (index.js) build/index.js
test command:
git repository:
author: Your Name
license: (ISC)Code language: plaintext (plaintext)

Take note of the entry point. We don’t have a folder named build just yet, but it’s coming soon. We’re not done yet, though. There’s more to add to our package.json file. To process our build chain we will use NPM to install the wp-scripts package. Enter the command below:

npm install --save-dev --save-exact @wordpress/scriptsCode language: Bash (bash)

After NPM downloads and installs the required software, you should have a node_modules folder added to the parent plugin. The install command also added some dependencies to our package.json file. Let’s open it up and have a look. We also will add some code to finalize our build process while we’re there. When you open up the file you should notice the following:

"devDependencies": {
    "@wordpress/scripts": "12.1.1"
  }Code language: JSON / JSON with Comments (json)

With our dependencies in place, let’s add our start and build scripts. Find the scripts section of package.json, it should look the same as below.

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
  },Code language: JSON / JSON with Comments (json)

Lets modify that slightly, change the scripts section to the following:

"scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start",
    "format:js": "wp-scripts format-js",
    "lint:js": "wp-scripts lint-js",
    "packages-update": "wp-scripts packages-update"
  },Code language: JSON / JSON with Comments (json)

It’s time to pat yourself on the back. our package.json file is complete! Before we move on, let’s make sure our index.asset.php file is created by entering following the build command:

npm run buildCode language: Bash (bash)

Writing Our Custom Gutenberg Block JavaScript

The WP scripts dependency expects us to have a src folder within our block. Go ahead and create a src folder and index.js file with the commands below:

mkdir src
touch src/index.jsCode language: Bash (bash)

Now use your code editor to add the following JavaScript to src/index.js.

import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';

const blockStyle = {
    backgroundColor: '#222',
    color: '#fff',
    padding: '20px',

registerBlockType( 'my-blocks/hello-world', {
    title: __( 'Hello World', 'my-blocks' ),
    icon: 'smiley',
    category: 'my-blocks',
    example: {},
    edit() {
        return (
            <div style={ blockStyle }>
                Hello World Witnin the editor.
    save() {
        return (
            <div style={ blockStyle }>
                Hello World on the front end.
} );Code language: JavaScript (javascript)

There are a few things going on in the code above, so we will take a moment to go over everything. First, we need a helping hand from wp-scripts. To get everything set up, we import our __ translation function from il8n, followed by registerBlockType from blocks.

For some visual flair, we’ll declare a constant variable (blockStyle) to hold our CSS. We’ve went with a simple dark grey background with white text to ensure our block stands out.

We then call the registerBlockType function, passing along some custom variables for our plugin (title, icon, and category). Next comes our output functions. The edit function will output in the Gutenberg editor, whereas the save function outputs to the frontend.

Last but not least, we’ll run the build command one final time to push our script changes out to production. Enter the following command in your terminal:

npm run buildCode language: Bash (bash)

Now, head over to the backend of your WordPress site and create or edit a post. You should see your block in the “layout” dropdown. You may need to do a hard refresh on your browser to see your block.

In our next tutorial, we’ll create our own dropdown section to hold our custom Gutenberg blocks and begin working on our first editable block. I hope to see you there! Do you have any advice or tips when it comes to developing custom blocks? Let me know in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *