Creating a custom Gutenberg block

With the evolution of the Gutenberg editor in WordPress, the creation of custom blocks has become a must for developers wishing to enrich the user experience and personalize content.

In this tutorial, we’ll learn step-by-step how to create a Gutenberg plugin that adds a custom “Hello World” block to the WordPress editor.

Objective: understand the basics of creating a Gutenberg block.

Public: beginner/intermediate developers with notions of PHP and JavaScript.

We’ll look at :

  • How to prepare the WordPress development environment.
  • How to structure a WordPress plugin.
  • How to register a Gutenberg block in PHP.
  • How to compile and test the block in the editor.

At the end of the tutorial, you will have :

  • A working plugin adding a “Hello World” block to WordPress.
  • An understanding of the basics for creating your own more advanced blocks.
  • A clear and structured method for developing other Gutenberg features.

Step 1: Install/Configure WordPress locally

1. Choose a local development tool

The solution chosen for this exercise will be to install WordPress locally with Local.

  • Creating a new WordPress site:
create a new site local
  • For example, name it “demo-gutenberg-block”:
new site name local
  • You can stay on “Preferred”:
preferred option local
  • Enter the connection information you require and click on “Add Site”:
set up worpress site local
  • The site was created:
created site local

2. Connect to the site Dashboard

  • Log in with the user name and password you chose when you created your account:
connecting to a wordpress site
  • This brings us to our site’s Dashboard:
wordpress site dashboard

Step 2 : Creating the plugin base

1. Create plugin folder

When the site was created, folders and files were created in the “Local Sites” folder (located where Local was installed on the workstation).

The Local sites > demo-gutenberg-block folder therefore contains all the folders that make up our newly-created site.

We’ll find the app/, conf/ and logs/ folders.

  • Go to ..app > public > wp-content > plugins and create our plugin folder, here named my-first-block-hello-world :
plugin folder creation
  • Then open the project folder in a code editor (here we’ll use Visual Studio Code):
project folder in visual studio code

2. Create the plugin’s main file

  • Create a PHP file named “my-first-block-hello-world.php” in the app > wp-content > plugins > my-first-block-hello-world folder:
create my first block hello world php
  • In this file, add a comment with information about our plugin:
<?php /** * Plugin name : My First Block Hello World * Plugin URI : https://example.com * Description : Example of a plugin to create a Gutenberg block "Hello World" * Version : 1.0.0 * Auteur :      Your name * Author URI : https://example.com * Text Domain : my-first-block-hello-world */

In this commentary :

  1. Plugin Name: the name displayed in the WordPress “Extensions” interface.
  2. Plugin URI (optional): can point to an official website or a Git repository.
  3. Description: brief description that appears in the list of plugins.
  4. Version: Indicates the version of your plugin.
  5. Author / Author URI: our name and possibly our website.
  6. Text Domain: text domain name for translation (important if you’re managing translations).
  • We add a function to block access to the file:
<?php /** * Plugin name : My First Block Hello World * Plugin URI : https://example.com * Description : Example of a plugin to create a Gutenberg block " Hello World " * Version : 1.0.0 * Author :      Your Name * Author URI : https://example.com * Text Domain : my-first-block-hello-world */ */ if ( ! defined('ABSPATH')) { exit ; }

This feature is a security measure commonly used in WordPress plugin and theme files. It prevents direct access to the file by a malicious user.

3. Plugin activation

  • On the site Dashboard, in the Plugins ⇒ Installed Plugins menu, you’ll see the plugin you’ve just created:
installed plugins menu
  • Click on “Activate” to activate it, and you’ll get the “Plugin activated” message:
plugin activated

Step 3 : Configuring the block structure

1. Creating a dedicated source code folder

In the plugin folder my-first-block-hello-world, we’ll create a folder called “src”.

It will contain all the JS (and possibly CSS/SCSS) code for the future block.

src folder creation

2. Using a build tool

To create a modern Gutenberg block, we often use Node.js and tools such as Babel or Webpack. This enables :

  • Write ESNext code (import, const, arrow functions, etc.).
  • Write JSX (a syntax similar to React).
  • Compile into a single “compatible” JS file for the editor and/or front-end.

It’s possible to do this without a build tool, but we prefer to use one with the recommended @wordpress/scripts method.

3. Use @wordpress/scripts

  • WordPress offers an official @wordpress/scripts package for easy Webpack/Babel configuration.
  • You can install it (via npm) in the plugin folder, then use it to compile the code.
  • This is the recommended method for creating Gutenberg blocks (and the most common).

4. Initialize npm and install @wordpress/scripts

  • Ensure that Node.js and npm are installed on the workstation:
node -v npm -v

Versions will appear if they are installed, otherwise install them.

  • Initialize the Node project.

Open a terminal in the folder my-first-block-hello-world and use the command :

npm init -y

This will create a “package.json” file:

package json

It contains the basic settings and we will change “type” : “commonjs” to “type” : “module”) :

{ "name" : "my-first-block-hello-world", "version" : "1.0.0", "main" : "index.js", "scripts" : { "test" : "echo "Error : no test specified" && exit 1" }, "keywords" : [], "author" : "", "licence" : "ISC", "type" : "module", "description" : "" }
  • Install @wordpress/scripts.

Still in the same folder, run the command :

npm install --save-dev @wordpress/scripts

This adds @wordpress/scripts to the development dependencies. It also creates the node_modules/ folder and the “package-lock.json” file:

node modules folder
  • Update the script in the package.json :

In the package.json file, we see that there is now a “devDependencies” section, which lists @wordpress/scripts :

{"name" : "my-first-block-hello-world", "version" : "1.0.0", "main" : "index.js", "scripts" : { "test" : "echo "Error : no test specified" && exit 1" }, "keywords" : [], "author" : "", "licence" : "ISC", "type" : "module", "description" : "", "devDependencies" : { "@wordpress/scripts" : "^30.9.0" } }

We’re going to add 2 handy commands to the “scripts” section:

-start: launches a “development” mode (with automatic rebuild when files are modified).

-build: generates the final, optimized version of the JS (and possibly the CSS).

{"name" : "my-first-block-hello-world", "version" : "1.0.0", "main" : "index.js", "scripts" : { "test" : "echo "Error : no test specified" && exit 1", "start" : "wp-scripts start", "build" : "wp-scripts build" }, "keywords" : [], "author" : "", "licence" : "ISC", "type" : "module", "description" : "", "devDependencies" : { "@wordpress/scripts" : "^30.9.0" } }

5. Creating the main block file (JS)

  • Here we are going to create, in the “src” folder, a file named “index.js” which will contain the code of the Gutenberg block:
index js creation
  • Start local compilation

In the terminal (always from the plugin folder), run the command :

npm run build

A build folder is created:

build folder

6. Creating CSS files

To style the block, we can add the following to the src/ folder:

  • editor-style.css : editor-specific styles (in Gutenberg).
  • style.css : styles for the front-end.
creation of css files

Import these files into “index.js” so that the build takes them into account. For example :

import './editor-style.css' ; import './style.css' ;

We’ll add a minimum number of styles to “editor-style.css”:

.wp-block-mpbhw-hello-world { background-color : #bbb27e ; padding : 10px ; }

And add some in “style.css” :

.wp-block-mpbhw-hello-world { border : solid 2px #e9ec10 ; padding : 10px ; }

Step 4 : Save block on PHP side

1. Understanding the function register_block_type()

Since WordPress 5.0, the blocks API provides the register_block_type() function. It enables :

  • Declare a block identifier (e.g. my-plugin/hello-world).
  • Indicate which JS script should be loaded into the editor.
  • Indicate which CSS files are to be loaded (editor-side and front-end), if required.

See the documentation for more details on the register_block_type().

2. Create an init function for the block

  • In the main plugin file, “my-first-block-hello-world.php”, under the header, create a function (e.g. “mpbhw_register_block()”) for everything to do with block registration.
  • Then hang this function on the action “init”.
<?php /** * Plugin Name : My First Block Hello World * Plugin URI : https://example.com * Description : Example of a plugin to create a Gutenberg block "Hello World" * Version : 1.0.0 * Auteur :      Votre nom * Author URI : https://example.com * Text Domain : my-first-block-hello-world */ if ( ! defined('ABSPATH')) { exit ; } function mpbhw_register_block() { // 1. save editor script wp_register_script( 'mpbhw-editor-script', plugins_url('build/index.js', __FILE__), array('wp-blocks', 'wp-element', 'wp-editor'), filemtime(plugin_dir_path(__FILE__) . 'build/index.js') ) ; // 2. check and save the editor style sheet $editor_css_file = plugin_dir_path(__FILE__) . 'build/index.css' ; if (file_exists($editor_css_file)) { wp_register_style('mpbhw-editor-style', plugins_url('build/index.css', __FILE__), array('wp-edit-blocks'), filemtime($editor_css_file) ) ; } // 3. vérifier et sauvegarder la feuille de style pour le front-end $front_css_file = plugin_dir_path(__FILE__) . 'build/style-index.css' ; if (file_exists($front_css_file)) { wp_register_style('mpbhw-front-style', plugins_url('build/style-index.css', __FILE__), array(), filemtime($front_css_file) ) ; } // 4. Save the block with its assets register_block_type('mpbhw/hello-world', array('editor_script' => 'mpbhw-editor-script', // JS for the editor 'editor_style' => 'mpbhw-editor-style', // CSS for the editor (if présent) 'style' => 'mpbhw-front-style', // CSS for the frontend (if présent) )) ; } // Run the function when WordPress starts add_action('init', 'mpbhw_register_block') ;

Explications

  1. wp_register_script():
    • Associates a handle (” mpbhw-editor-script “) to a JS file and its dependencies..
    • plugins_url( ‘build/index.js’, FILE ) returns the URL to the compiled JS file.
    • filemtime(…) is a cache management trick (it generates a unique version for each modification).
  2. wp_register_style():
    • The same goes for a CSS file, if you have one for the editor and/or front-end.
    You can comment on these lines if you don’t use separate CSS.
  3. register_block_type():
    • Declares block identifier (mpbhw/hello-world) by linking the editor script (editor_script) and, if necessary, styles (editor.css et styles.css).
  4. add_action( ‘init’, … ):
    • We attach the registration function to the init hook, so that WordPress knows when to load the block.

3. Check block registration

  1. Activate or reactivate the plugin (if you haven’t already done so).
  2. Check in WordPress admin → no error message should appear.
  3. If all goes well, WordPress now knows that there’s a “mpbhw/hello-world” block and that it should load “index.js” when editing an article.

At this stage

  • The block won’t be visible in the editor yet, as we haven’t finished coding the logic in index.js.
  • But the technical registration on the PHP side is in place.

Step 5 : Creating the block on the JavaScript side (Gutenberg)

1. Import dependencies

In the “src/index.js” file, we import the functions and modules we need for a basic block:

import { registerBlockType } from '@wordpress/blocks' ; import { __ } from '@wordpress/i18n' ; import { useBlockProps } from '@wordpress/block-editor' ;
  • @wordpress/blocks: saves the block (registerBlockType).
  • @wordpress/i18n: allows you to use the __() function for translation (optional).
  • @wordpress/block-editor: brings together various components and hooks for the editor (ex. useBlockProps, InspectorControls, ColorPalette, etc.).

2. Declaring the block with registerBlockType

Still in src/index.js, we call registerBlockType(‘namespace/slug’, config) to define our block:

import './editor-style.css' ; import './style.css' ; import { registerBlockType } from '@wordpress/blocks' ; import { __ } from '@wordpress/i18n' ; import { useBlockProps } from '@wordpress/block-editor' ; registerBlockType('mpbhw/hello-world', { title : __('Hello World', 'my-first-block-hello-world'), icon : 'universal-access-alt', // icône personnalisée WP ou SVG category : 'text', // Block categories (common, text, widgets, etc.) // Attributes (data to be stored). attributes : { message : { type : 'string', default : 'Hello World!', }, }, // The function that manages the editing interface (on the Gutenberg editor side). edit : (props) => { // Extract or read the value of the const attribute { attributes, setAttributes } = props ; const { message } = attributes ; // Gutenberg Hook to manage HTML container props const blockProps = useBlockProps({ className : 'wp-block-mpbhw-hello-world' }) ; // Add style editor-style.css from block to editor // Editor's rendering return ( <div {...blockProps}> <h2>Hello from the editor!</h2> <input type="text" value={props.attributes.message} onChange={(e) => setAttributes({ message : e.target.value })}> </div> ) ; }, // The function that manages final rendering (on the front end). save : (props) => { const { attributes } = props ; const { message } = attributes ; // Use `useBlockProps.save()` if you want to keep the CSS classes of WP. const blockProps = useBlockProps.save() ; // The final HTML rendering returns ( <div {...blockProps}> <p>{props.attributes.message}</p> </div> ) ; }, }) ;

Explications

  • mpbhw/hello-world: the block’s unique identifier (namespace + slug). It must correspond to the one declared in PHP: register_block_type(‘mpbhw/hello-world’, … ).
  • title: the name that appears in the list of blocks.
  • icon: the block icon (can be a Dashicon icon name, an object, or an SVG).
  • category: the category in which the block appears (e.g. common, text, media, widgets, embed, etc.).
  • attributes: definition of the data to be stored by the block (in this case, a message text string).
  • edit(props): the React component for the Gutenberg editor.
    • props.attributes : retrieves current attributes.
    • props.setAttributes() : updates attributes.
    • useBlockProps() : hook to manage internal classes and data for the block.
  • save(props): the function that generates the final HTML of the block on the front.
    • Attribute content is transformed into HTML tags.

(Here, we use a simple to modify the value. You can also use a Gutenberg component such as TextControl, RichText, etc.)

Compiler et tester

Execute in terminal :

npm run build

This will generate (or regenerate) compiled files in the build/ folder:

build css files
  • Check in WordPress (under Plugins) that the plugin is activated.
  • Open the Gutenberg editor (for example, create a new article):
article creation
  • Search for “Hello World” among the blocks and add it:
hello world block added

We obtain the block with a <h2>Hello from the editor</h2> and an <input> which will allow us to enter our text. We also have the background color defined in “editor-style.css” and the border defined in “style.css”.

This code is found in src/index.js for editor-side rendering:

       return ( <div {...blockProps}> <h2>Hello from the Editor!</h2> <input type="text" value={props.attributes.message} onChange={(e) => setAttributes({ message : e.target.value })} /> </div>

And for front-end rendering, in “src/index.js” :

       return ( <div {...blockProps}> <p>{props.attributes.message}</p> </div>
  • Enter new text in the <input> :
enter a new text in the input field
  • Check preview. We obtain our message in a <p>, as well as the style that comes from “style.css” :
article preview

Congratulations

You’ve followed all the steps required to create a Gutenberg custom block in WordPress, from setting up the environment to integrating styles and managing rendering.

Your “Hello World” block is now fully functional:

  • It is saved and appears in the block library.
  • It allows the user to enter text and displays it on the front-end.
  • It applies specific styles in the editor and on the site.

This gives you the basis for developing more advanced blocks!

Further information

Now that you’ve mastered the basics, here are a few ways to make your block more interactive and powerful:

  • Use “RichText” for formatted text: currently, the block uses a simple <input>. You can replace this with a rich text editor using the”RichText” componant from Gutenberg. This will allow you to enter text in bold, italics or with links. etc.
  • Add customization options (“InspectorControls”): you can add an options panel to the Gutenberg sidebar so that the user can customize the block. This allows the user to choose a text color, change its alignment or add a background color.
  • Create a more complex block (gallery, list, quotations, etc.): it’s possible to make a more advanced block, such as a custom gallery, dynamic counter or accordion.

You now have all the tools you need to create custom blocks with Gutenberg!

WordPress is constantly evolving, so keep experimenting, add new features and explore the official WordPress documentation to find out more.

Leave a Reply

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