This tutorial covers how to set up a custom Gutenberg block plugin in WordPress using Underpin. This plugin can hold all of your site’s custom Gutenberg blocks, and allows you to bring your custom blocks with you when you change themes in the future.
Install Underpin and Set Up Your Plugin
The first thing you’ll need to-do is set up and install Underpin, as well as your plugin. If you aren’t familiar with how to-do that, check out this guide. It will walk you through setting up Underpin and your plugin using a boilerplate.
The rest of this lesson will assume that you named your plugin custom_blocks
. If you used something different than that, be sure to adjust your code to call your own function instead of custom_blocks
. Got it? Great, let’s get started!
Set Up The Loaders
With Underpin, everything is registered using an Underpin loader. These loaders will handle actually loading all of the things you need to register. Everything from scripts, to blocks, even admin pages, can all be added directly using Underpin’s loaders. Loaders make it so that everything uses an identical pattern to add items to WordPress. With this system, all of these things use nearly exact same set of steps to register.
To build a Gutenberg block, we need to add at least two loaders, but you usually end up needing three.
- A block loader
- A script loader
- A style loader (optional)
Create the Gutenberg Block Loader
First things first, install the block loader. In your command line, navigate to your mu-plugins
directory and run this command:
composer require underpin/block-loader
This will install the loader necessary to register blocks in Underpin. Now that it’s installed, you can register your block by chaining custom_blocks
like so:
Let’s break down what’s going on above.
custom_blocks()
actually retrieves this plugin’s instance of Underpinblocks()
Retrieves the loader registry for this instance of Underpinadd()
actually adds this block to the registry
Behind the scenes, Underpin will automatically create an instance of Block, which then automatically runs register_block_type
using the provided args
and type
.
At this point, your plugin’s bootstrap.php
will look like this:
Create the Gutenberg Block Script
Next up, install the script loader. In your command line, navigate to your mu-plugins
directory and run this command:
composer require underpin/script-loader
Exactly like blocks, this will install the loader necessary to register scripts in Underpin. With it, you can register scripts like so:
Let’s break down what’s going on above.
custom_blocks()
actually retrieves this plugin’s instance of Underpinscripts()
Retrieves the loader registry for this instance of Underpinadd()
actually adds this script to the registrycustom_blocks()->js_url()
is a helper function that automatically gets the javascript url for this plugin. This is configured in thecustom_blocks
function directly, and defaults tobuild
Behind the scenes, Underpin will automatically create an instance of Script, which then automatically runs wp_register_script
using the arguments passed into the registry.
Enqueuing the Script
Now that the script is registered, you actually have to enqueue the script as well. We could manually enqueue the script, but instead we’re going to use Underpin’s Middleware functionality to automatically enqueue this script in the admin area.
Your bootstrap.php
file should now look something like this:
Create the Blocks Javascript File
First, you need to modify your webpack.config.js
to create a new entry file. It should look like this:
This instructs Webpack to take a JS file located in your plugin’s src
directory, and compile it into build/custom-blocks.js
. From here, we need to create a new file in the src
directory called custom-blocks.js
.
Now we have to register the block in our Javascript, as well. This will allow us to customize how this block behaves in the Gutenberg editor. In this lesson, we’re going to just create a very simple “Hello World” block.
Okay, so what’s going on here?
- We’re importing
registerBlockType
so we can use it in this file - We’re also importing
__
so we can make translate-able strings - We are running
registerBlockType
to register our “Hello World” block to the editor.
Now run npm install
and npm run start
. This will create two files in your build
directory:
- custom-blocks.js – This is your compiled Javascript file that gets enqueued by Underpin’s script loader.
- custom-blocks-asset.php – This asset file tells WordPress what additional scripts need enqueued in-order for this script to work properly.
You will notice that we did not install @wordpress/blocks
or @wordpress/i18n
. That’s not a mistake. Since these are internal WordPress scripts, we need to tell WordPress to enqueue those scripts before our script. Fortunately, WordPress and Underpin make this pretty easy to-do.
Update Underpin Script to include
Back in bootstrap.php
, update your script’s add
function to include a deps
argument. Since this argument is a path, it will automatically require the file, and use it to tell WordPress which scripts need enqueued. Since Webpack automatically generates this file for us, we no-longer need to worry about adding dependencies every time we want to use a WordPress library.
From your admin screen, if you navigate to posts>>Add New, you will find that you can use a new block called “Hello World”, which will simply display “Hello World” In gigantic letters on the page.
With this script, you can create as many blocks as you need by simply creating another registerBlockType
call, and registering the block though Underpin using custom_blocks()->blocks()->add
.
Create the Gutenberg Block Stylesheet (Optional)
Stylesheets need a bit of extra thought in-order for them to work-as expected. Normally, you would simply enqueue the script much like you enqueue a script. The catch is that this stylesheet also needs to be used in the block editor in-order to accurately display the block output. Let’s get into how to set that up.
Just like everything else with Underpin, the first step is to install the appropriate loader, the register the style.
In your mu-plugins
directory, run:
composer require underpin/style-loader
From there, register a style in your bootstrap.php
file:
Then, update webpack.config.js
to include custom-block-styles.css
, like so:
Next, update your registered block to use the style to specify the stylesheet to be used with this block like so:
That will update your block to enqueue the stylesheet in the block editor automatically, and will reflect the styles in the stylesheet. This will work both on the actual site and the block editor.
With the style set as-such:
You’ll get this in the block editor, and the front end:
Use Server-Side Rendering (Optional)
This is all fine and dandy, but there’s one problem with how this is built – what happens if a theme needs to change the markup of our block? Or, what if, for some reason, it makes more sense to use PHP to render this block instead of Javascript?
A fundamental problem with blocks is that it will hardcode the saved block result inside of the WordPress content. In my opinion, it’s better to render using server-side rendering. This tells WordPress that, instead of saving the HTML output, to instead create a placeholder for the block, and just before the content is rendered, WordPress will inject the content from a PHP callback. This allows you to update blocks across your site quickly just by updating a PHP callback whenever you want.
Call me old fashioned, but I think that’s a lot more maintain-able, and thankfully it’s pretty easy to-do.
First, update your registered block so that save
returns null
. This instructs the editor to simply not save HTML, and just put a placeholder there instead.
Now, if you specify a render_callback
in your registered block arguments, it will use the callback instead of what was originally in the save
callback.
Now if you look in your editor, you’ll still see “Hello World”, because that’s what the Javascript’s edit
method returns, however, if you save and look at the actual post, you’ll find that the actual post will show “Hey, this is a custom callback” instead. This is because it’s using PHP to render the output on the fly. Now, if you change the content of the render_callback
, it will automatically render this output.
Going Further – Use Underpin’s Template System
What happens if you have a WordPress theme, and you want to actually override the render callback? A good way to approach this is to use Underpin’s built-in Template loader system. This system allows you to specify file locations for PHP templates that render content, and also has baked-in support for template overriding by themes.
Underpin’s template system is a PHP trait. It can be applied to any class that needs to output HTML content. The tricky part is, we haven’t made a class yet, have we?
…Have we?
Well, actually, we have. Every time we run the add
method in WordPress, it automatically creates an instance of a class, and it uses the array of arguments to construct our class for us. However, now, we need to actually make the class ourselves so we can apply the Template trait to the class, and render our template. So, next up we’re going to take our registered block, and move it into it’s own PHP class, and then instruct Underpin to use that class directly instead of making it for us.
First up, create a directory called lib
inside your plugin directory, and then inside lib
create another directory called blocks
. Inside that, create a new PHP file called Hello_World.php
. Underpin comes with an autoloader, so the naming convention matters here.
├── lib
│ └── blocks
│ └── Hello_World.php
Inside your newly created PHP file, create a new PHP class called Hello_World
that extends Block
, then move all of your array arguments used in your add
method as parameters inside the class, like so:
Then, replace the array of arguments in your add
callback with a string that references the class you just created, like so:
By doing this, you have instructed Underpin to use your PHP class instead of creating one from the array of arguments. Now that we have a full-fledged PHP class in-place, we can do a lot of things to clean this up a bit, and use that template Trait I mentioned before.
Add use \Underpin\Traits\Templates
to the top of your PHP class, and add the required methods to the trait as well, like so:
Now, we’re going to fill out each of these functions. get_templates
should return an array of template file names with an array declaring if that template can be manipulated by a theme, or not, like so:
get_template_group
should return a string, that indicates what the template sub directory should be called. In our case, we’re going to make it hello-world
.
get_template_root_path
should simply return custom_blocks()->template_dir()
, as we don’t need to use a custom template directory or anything.
Finally, we have the option to override the template override directory name into something specific to our own plugin. Let’s do that, too:
With these three items in-place, you can now create a new file in templates/hello-world
called wrapper.php
. Inside your theme, this template can be completely overridden by adding a file in custom-blocks/hello-world
called wrapper.php
. Let’s start by adding our template in the plugin file.
The first thing your template needs is a header that checks to make sure the template was loaded legitimately. You don’t want people to load this template outside of the intended way, so you must add a check at the top level to make sure it was loaded properly, like so:
Underpin automatically creates a new variable called $template
and assigns it to the class that renders the actual template. So inside your template file $template
will always be the instance of your registered block. This allows you to create custom methods inside the block for rendering purposes if you want, but it also gives you access to rendering other sub-templates using $template->get_template()
, plus a lot of other handy things that come with the Template
trait. As you can see above, this also provides you with a handy way to validate that the required file is legitimate.
Now, simply add the HTML output at the bottom, like this:
From there, go back into your Hello_World
class, and update the render callback to use your template. This is done using get_template
, like so:
This instructs the render_callback
to use get_template
, which will then retrieve the template file you created. If you look at your template’s output, you’ll notice that your h1 tag changed to read “Hey, this is a custom callback, and it is inside my template!”.
Now, go into your current theme, create a php file inside custom-blocks/hello-world
called wrapper.php
. Copy the contents of your original wrapper.php
file, and paste them in. Finally, change the output a little bit. When you do this, the template will automatically be overridden by your theme.
Conclusion
Now that you have one block set-up, it’s just a matter of registering new blocks using Underpin, and inside your Javascript using registerBlockType
. If necessary, you can create a block class for each block, and use the template system to render the content.
This post barely scratches the surface of what can be done with Underpin, the template loader, and Gutenberg. From here, you could really flesh out your block into something more than a trivial example. If you want to go deeper on these subjects, check out my WordPress plugin development course, where we create a block much like how I describe it here, and then build out a fully-functional Gutenberg block, as well as many other things.
Leave a Reply