Create a Custom Gutenberg Block Plugin with Underpin
Tags: wordpress, gutenberg, javascript, php
Published: 2021-07-02 14:23:40

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

If you haven’t already, the first thing you need to-do is actually _install Underpin_ on your site. Underpin uses Composer to handle its dependencies, and it can be loaded in as a [Must-Use plugin](https://wordpress.org/support/article/must-use-plugins/), or directly inside of your plugin. It really just depends on where you have Composer set up on your site. I like to install it as a mu-plugin for personal sites, and directly inside the plugin when I’m building a plugin that is intended to be distributed. In this lesson, I’ll assume you’re going to use it as a must-use plugin.

If you already have Underpin installed, you can skip this entirely.

Inside the `wp-content` directory, create a new directory called mu-plugins. If `mu-plugins` already exists, then skip this step.

Now create a `composer.json` file inside the `mu-plugins` directory. Most of this is the default settings for a `composer.json` file, the key difference is that `extra.installer-paths` is tweaked to force `wordpress-muplugins` to simply be installed directly in the `vendor` directory. This is necessary because Underpin is considered a `mu-plugin` by Composer, and will install in an improper directory, otherwise.

```json
{
    "name": "wpda/muplugin",
    "type": "plugin",
    "require": {},
    "extra":{
    	"installer-paths": {
	      "vendor/{$vendor}/{$name}": ["type:wordpress-muplugin"]
	}
    }
}
```

Next, create a new PHP file inside the `mu-plugins` directory. It can named be whatever you want it to be. WordPress will automatically include this file, and run it on every page load. This happens _really early_ in WordPress’s runtime so there are some limitations to this, but for our needs it’s perfect.

Use this code to include composer’s autoloader. This will automatically install and set up Underpin so you can use it anywhere else. This includes any plugins custom to this site, or your theme. Essentially, this makes Underpin as close to core functionality as possible. The only caveat is you have to _remember to upload the mu-plugins directory to your live site._

```php
<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Load Underpin, and its dependencies.
$autoload = plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';

require_once( $autoload );
```

Now, let’s install Underpin. Open up your command-line, navigate to your site’s `mu-plugins` directory, and then run this command:

`composer require underpin/underpin`

Booya! You now have Underpin installed. Now, let’s use it in a plugin.

## Set Up The Gutenberg Block Plugin

A fundamental practice of WordPress is to create WordPress plugins for each piece of custom functionality for your site. This allows you to enable/disable these plugins as-needed, and potentially re-use the plugin on other sites in the future.

We’re going to use [Underpin’s plugin boilerplate](https://github.com/Underpin-WP/underpin-plugin-boilerplate) to help set up this plugin quickly. This plugin does a few key things for us:

1.  It sets up our Underpin plugin instance
2.  It comes with a WordPress-ready Webpack config
3.  It sets up the file headers that WordPress needs in-order to recognize the file as a plugin

To use this, navigate to `wp-content/plugins` and clone the boilerplate. Then you’ll need to-do a few find/replaces in the boilerplate.

1.  Replace `plugin-name-replace-me` with `custom-blocks` (it can be whatever you want, just make sure spaces use dashes, and it’s all lowercase)
2.  Replace `Plugin Name Replace Me` with `Custom Blocks` (Again, whatever you want just has to use spaces and Title Case)
3.  Replace `plugin_name_replace_me` with `custom_blocks` (Same thing applies here, but you should use snake_case)
4.  Replace `Plugin_Name_Replace_Me` with `Custom_Blocks` (using Upper_Snake_Case)

At this point, your plugin’s `bootstrap.php` file should look something like this:

```php
<?php
/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/

use Underpin\Abstracts\Underpin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */
function custom_blocks() {
	return Underpin::make_class( [
		'root_namespace'      => 'Custom_Blocks',
		'text_domain'         => 'custom_blocks',
		'minimum_php_version' => '7.0',
		'minimum_wp_version'  => '5.1',
		'version'             => '1.0.0',
	] )->get( __FILE__ );
}

// Lock and load.
custom_blocks();
```

## 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.

1.  A block loader
2.  A script loader
3.  A style loader (optional)

### Create the Gutenberg Block Loader

First things first, install the [block loader](https://github.com/Underpin-WP/underpin-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:

```php
// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', [
	'name'        => 'My Custom Block',                       // Names your block. Used for debugging.
	'description' => 'A custom block specific to this site.', // Describes your block. Used for debugging
	'type'        => 'custom-blocks/hello-world',             // See register_block_type
	'args'        => [],                                      // See register_block_type
] );
```

Let’s break down what’s going on above.

1.  `custom_blocks()` actually retrieves this plugin’s instance of Underpin
2.  `blocks()` Retrieves the loader registry for this instance of Underpin
3.  `add()` actually _adds_ this block to the registry

Behind the scenes, Underpin will automatically create an instance of [Block](https://github.com/Underpin-WP/underpin-block-loader/blob/master/Block.php), 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:
```php
<?php
/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/

use Underpin\Abstracts\Underpin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */
function custom_blocks() {
	return Underpin::make_class( [
		'root_namespace'      => 'Custom_Blocks',
		'text_domain'         => 'custom_blocks',
		'minimum_php_version' => '7.0',
		'minimum_wp_version'  => '5.1',
		'version'             => '1.0.0',
	] )->get( __FILE__ );
}

// Lock and load.
custom_blocks();

// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', [
	'name'        => 'My Custom Block',                       // Names your block. Used for debugging.
	'description' => 'A custom block specific to this site.', // Describes your block. Used for debugging
	'type'        => 'custom-blocks/hello-world',             // See register_block_type
	'args'        => [],                                      // See register_block_type
] );
```

### Create the Gutenberg Block Script

Next up, install the [script loader.](https://github.com/Underpin-WP/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:

```php
custom_blocks()->scripts()->add( 'custom_blocks', [
	'handle'      => 'custom-blocks',                                // Script Handle used in wp_*_script
	'src'         => custom_blocks()->js_url() . 'custom-blocks.js', // Src used in wp_register_script
	'name'        => 'Custom Blocks Script',                         // Names your script. Used for debugging.
	'description' => 'Script that loads in the custom blocks',       // Describes your script. Used for debugging.
] );
```

Let’s break down what’s going on above.

1.  `custom_blocks()` actually retrieves this plugin’s instance of Underpin
2.  `scripts()` Retrieves the loader registry for this instance of Underpin
3.  `add()` actually _adds_ this script to the registry
4.  `custom_blocks()->js_url()` is a helper function that automatically gets the javascript url for this plugin. This is configured in the `custom_blocks` function directly, and defaults to `build`

Behind the scenes, Underpin will automatically create an instance of [Script](https://github.com/Underpin-WP/underpin-block-loader/blob/master/Block.php), 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](https://github.com/underpin-WP/underpin#middleware) functionality to automatically enqueue this script in the admin area.

```php
custom_blocks()->scripts()->add( 'custom_blocks', [
	'handle'      => 'custom-blocks',                                // Script Handle used in wp_*_script
	'src'         => custom_blocks()->js_url() . 'custom-blocks.js', // Src used in wp_register_script
	'name'        => 'Custom Blocks Script',                         // Names your script. Used for debugging.
	'description' => 'Script that loads in the custom blocks',       // Describes your script. Used for debugging.
	'middlewares' => [
		'Underpin_Scripts\Factories\Enqueue_Admin_Script',             // Enqueues the script in the admin area
	],
] );
```

Your `bootstrap.php` file should now look something like this:

```php
<?php
/*
Plugin Name: Custom Blocks
Description: Plugin Description Replace Me
Version: 1.0.0
Author: An awesome developer
Text Domain: custom_blocks
Domain Path: /languages
Requires at least: 5.1
Requires PHP: 7.0
Author URI: https://www.designframesolutions.com
*/

use Underpin\Abstracts\Underpin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Fetches the instance of the plugin.
 * This function makes it possible to access everything else in this plugin.
 * It will automatically initiate the plugin, if necessary.
 * It also handles autoloading for any class in the plugin.
 *
 * @since 1.0.0
 *
 * @return \Underpin\Factories\Underpin_Instance The bootstrap for this plugin.
 */
function custom_blocks() {
	return Underpin::make_class( [
		'root_namespace'      => 'Custom_Blocks',
		'text_domain'         => 'custom_blocks',
		'minimum_php_version' => '7.0',
		'minimum_wp_version'  => '5.1',
		'version'             => '1.0.0',
	] )->get( __FILE__ );
}

// Lock and load.
custom_blocks();

// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', [
	'name'        => 'My Custom Block',                       // Names your block. Used for debugging.
	'description' => 'A custom block specific to this site.', // Describes your block. Used for debugging
	'type'        => 'underpin/custom-block',                 // See register_block_type
	'args'        => [],                                      // See register_block_type
] );

custom_blocks()->scripts()->add( 'custom_blocks', [
	'handle'      => 'custom-blocks',                                // Script Handle used in wp_*_script
	'src'         => custom_blocks()->js_url() . 'custom-blocks.js', // Src used in wp_register_script
	'name'        => 'Custom Blocks Script',                         // Names your script. Used for debugging.
	'description' => 'Script that loads in the custom blocks',       // Describes your script. Used for debugging.
	'middlewares' => [
		'Underpin_Scripts\Factories\Enqueue_Admin_Script',             // Enqueues the script in the admin area
	],
] );
```

## 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:

```javascript
/**
 * WordPress Dependencies
 */
const defaultConfig = require( '@wordpress/scripts/config/webpack.config.js' );

/**
 * External Dependencies
 */
const path = require( 'path' );

module.exports = {
	...defaultConfig,
	...{
		entry: {
			"custom-blocks": path.resolve( process.cwd(), 'src', 'custom-blocks.js' ) // Create "custom-blocks.js" file in "build" directory
		}
	}
}
```

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.

```javascript
// Imports the function to register a block type.
import { registerBlockType } from '@wordpress/blocks';

// Imports the translation function
import { __ } from '@wordpress/i18n';

// Registers our block type to the Gutenberg editor.
registerBlockType( 'custom-blocks/hello-world', {
	title: __( "Hello World!", 'beer' ),
	description: __( "Display Hello World text on the site", 'beer' ),
	edit(){
		return (
			<h1 className="hello-world">Hello World!</h1>
		)
	},
	save() {
		return (
			<h1 className="hello-world">Hello World!</h1>
		)
	}
} );
```

Okay, so what’s going on here?

1.  We’re importing `registerBlockType` so we can use it in this file
2.  We’re also importing `__` so we can make translate-able strings
3.  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:

1.  **custom-blocks.js** – This is your compiled Javascript file that gets enqueued by Underpin’s script loader.
2.  **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.

```php
custom_blocks()->scripts()->add( 'custom_blocks', [
	'handle'      => 'custom-blocks',                                          // Script Handle used in wp_*_script
	'src'         => custom_blocks()->js_url() . 'custom-blocks.js',           // Src used in wp_register_script
	'name'        => 'Custom Blocks Script',                                   // Names your script. Used for debugging.
	'description' => 'Script that loads in the custom blocks',                 // Describes your script. Used for debugging.
	'deps'        => custom_blocks()->dir() . 'build/custom-blocks.asset.php', // Load these scripts first.
	'middlewares' => [
		'Underpin_Scripts\Factories\Enqueue_Admin_Script',                       // Enqueues the script in the admin area
	],
] );
```

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.

<figure class="wp-block-image size-large">![](https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_18-35.png)</figure>

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:

```php
custom_blocks()->styles()->add( 'custom_block_styles', [
	'handle'      => 'custom-blocks',                                        // handle used in wp_register_style
	'src'         => custom_blocks()->css_url() . 'custom-block-styles.css', // src used in wp_register_style
	'name'        => 'Custom Blocks Style',                                  // Names your style. Used for debugging
	'description' => 'Styles for custom Gutenberg blocks',                   // Describes your style. Used for debugging
] );
```

Then, update `webpack.config.js` to include `custom-block-styles.css`, like so:

```javascript
/**
 * WordPress Dependencies
 */
const defaultConfig = require( '@wordpress/scripts/config/webpack.config.js' );

/**
 * External Dependencies
 */
const path = require( 'path' );

module.exports = {
	...defaultConfig,
	...{
		entry: {
			"custom-blocks": path.resolve( process.cwd(), 'src', 'custom-blocks.js' ), // Create "custom-blocks.js" file in "build" directory
			"custom-block-styles": path.resolve( process.cwd(), 'src', 'custom-block-styles.css' )
		}
	}
}
```

Next, update your registered block to use the style to specify the stylesheet to be used with this block like so:

```php
// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', [
	'name'        => 'My Custom Block',                       // Names your block. Used for debugging.
	'description' => 'A custom block specific to this site.', // Describes your block. Used for debugging
	'type'        => 'custom-blocks/hello-world',             // See register_block_type
	'args'        => [                                        // See register_block_type
		'style' => 'custom-blocks',                             // Stylesheet handle to use in the block
	],
] );
```

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:

```css
.hello-world {
	background:rebeccapurple;
}
```

You’ll get this in the block editor, and the front end:

![](https://www.wpdev.academy/wp-content/uploads/2021/06/2021-06-29_20-50.png)

## 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._

```javascript
// Registers our block type to the Gutenberg editor.
registerBlockType( 'custom-blocks/hello-world', {
	title: __( "Hello World!", 'beer' ),
	description: __( "Display Hello World text on the site", 'beer' ),
	edit(){
		return (
			<h1 className="hello-world">Hello World!</h1>
		)
	},
	save: () => null
} );
```

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.

```php
// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', [
	'name'        => 'My Custom Block',                       // Names your block. Used for debugging.
	'description' => 'A custom block specific to this site.', // Describes your block. Used for debugging
	'type'        => 'custom-blocks/hello-world',             // See register_block_type
	'args'        => [                                        // See register_block_type
		'style' => 'custom-blocks',                             // Stylesheet handle to use in the block
		'render_callback' => function(){
			return '<h1 class="hello-world">Hey, this is a custom callback!</h1>';
		}
	],
] );
```

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](https://github.com/underpin-WP/underpin#template-system-trait). 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](https://www.php.net/manual/en/language.oop5.traits.php). 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:

```php
<?php

namespace Custom_Blocks\Blocks;


use Underpin_Blocks\Abstracts\Block;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Hello_World extends Block {

	public $name        = 'My Custom Block';                       // Names your block. Used for debugging.
	public $description = 'A custom block specific to this site.'; // Describes your block. Used for debugging
	public $type        = 'custom-blocks/hello-world';             // See register_block_type

	public function __construct() {
		$this->args        = [                                        // See register_block_type
			'style' => 'custom-blocks',                                 // Stylesheet handle to use in the block
			'render_callback' => function(){
				return '<h1 class="hello-world">Hey, this is a custom callback!</h1>';
			}
		];
		parent::__construct();
	}

}
```

Then, replace the array of arguments in your `add` callback with a string that references the class you just created, like so:

```php
// Registers block
custom_blocks()->blocks()->add( 'my_custom_block', 'Custom_Blocks\Blocks\Hello_World' );
```

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:
```php
<?php

namespace Custom_Blocks\Blocks;


use Underpin_Blocks\Abstracts\Block;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Hello_World extends Block {
	use \Underpin\Traits\Templates;

	public $name        = 'My Custom Block';                       // Names your block. Used for debugging.
	public $description = 'A custom block specific to this site.'; // Describes your block. Used for debugging
	public $type        = 'custom-blocks/hello-world';             // See register_block_type

	public function __construct() {
		$this->args        = [                                        // See register_block_type
			'style' => 'custom-blocks',                                 // Stylesheet handle to use in the block
			'render_callback' => function(){
				return '<h1 class="hello-world">Hey, this is a custom callback!</h1>';
			}
		];
		parent::__construct();
	}

	public function get_templates() {
		// TODO: Implement get_templates() method.
	}

	protected function get_template_group() {
		// TODO: Implement get_template_group() method.
	}

	protected function get_template_root_path() {
		// TODO: Implement get_template_root_path() method.
	}

}
```

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:

```php
public function get_templates() {
	return ['wrapper' => [ 'override_visibility' => 'public' ]];
}
```

`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`.

```php
protected function get_template_group() {
	return '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.

```php
protected function get_template_root_path() {
	return custom_blocks()->template_dir();
}
```

Finally, we have the _option_ to override the template override directory name into something specific to our own plugin. Let’s do that, too:

```php
protected function get_override_dir() {
	return 'custom-blocks/';
}
```

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:

```php
<?php

if ( ! isset( $template ) || ! $template instanceof \Custom_Blocks\Blocks\Hello_World ) {
	return;
}

?>
```

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:

```php
<?php

if ( ! isset( $template ) || ! $template instanceof \Custom_Blocks\Blocks\Hello_World ) {
	return;
}

?>
<h1 class="hello-world">Hey, this is a custom callback, and it is inside my template!</h1>
```

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:

```php
public function __construct() {
		$this->args = [                         // See register_block_type
			'style'           => 'custom-blocks', // Stylesheet handle to use in the block
			'render_callback' => function () {
				return $this->get_template( 'wrapper' );
			},
		];
		parent::__construct();
	}
```

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](https://www.wpdev.academy/course/beer-lister-plugin/), 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.

## Looking for more WordPress Resources?
Join WP [Dev Academy’s Discord server](https://discord.gg/ArqzZmqfYZ), and become a part of a growing community of WordPress developers.