As a WordPress developer who has mainly focused on enterprise level solutions, the concept of a multi-block made far more sense than having several individual block plugins. Thinking long term, a well-designed enterprise system could easily have dozens of custom blocks and other functionality related to modifying blocks or improving the editor experience.
The concept of a multi-block plugin is something I’ve seen mentioned online a couple of times, but I don’t recall seeing an in-depth tutorial. In this article, I will create a basic multi-block plugin then add advanced functionality to turn it into a robust block plugin that can be extended beyond registering blocks.
By the end of this article you will have gone through the steps to set yourself up with your own multi-block plugin and will be ready to build out and manage any number of blocks in one centralized plugin, along with other block editor related functionality such as variations, registering block styles, adding slot fills, and anything else you may need.
Table of Contents
Benefits to a Multi-Block Plugin
- Less maintenance, easier dependency management
- Increase shared code, reduce duplication across plugins
- Potential improvements to security response time (I’m no expert, but a hole in a key dependency is easier to address and contain in 1 plugin than 10)
If you are unfamiliar with creating custom blocks and the system setup, requirements to do so, please read up on creating a Block Development Environment in WordPress.
If you are building along with this tutorial there is an example of the plugin for reference that can be see in this public repo.
Basic Setup
By the end of this section of the tutorial you will have built a single plugin called WP Multi Block that will register two static blocks and a dynamic one. Let’s get started by creating your block plugin.
Creating a Block Plugin
The first step is to get a base plugin setup for blocks. For this, you will use the officially supported `create-block` tool. This tool will take care of scaffolding this plugin, all by running one simple command inside your /plugins
folder:
npx @wordpress/create-block@latest wp-multi-block
By default, the @wordpress/create-block
generates a static block, but there’s an argument --variant dynamic
that can be used if you wish to start with only a dynamic block. If you’re following along you’ll use this argument later in this tutorial.
What’s the difference between a static block and a dynamic block? At a very high level, static blocks use a save()
function to store HTML markup for the block into the post_content
table in our database. Dynamic blocks use a PHP render callback to render content on the server side when the page or post is viewed, only storing the block’s attributes in the database. For more details you can read here on the Developer Blog Static vs. dynamic blocks: What’s the difference?
For the purposes of this tutorial you will be using the basic command and starting with a static block.
Refactor the Plugin Structure
To better organize multiple blocks in the plugin there are some changes you can make to the plugin structure.
Create a Master Blocks Directory
Inside the src
directory do the following:
- Delete all existing files
- Create a folder called
blocks
Why add a blocks
directory, it seems unnecessary? We’ll want to be able to add a src/scripts
and maybe a src/styles
later, so grouping blocks will improve long-term organization and maintenance.
Create a Single Block
There’s a way we can use @wordpress/create-block
to set up only what is required for a block by using the --no-plugin
option. Inside of the src/blocks
directory and run the following:
npx @wordpress/create-block@latest block-one --no-plugin
Here’s what my updated directory structure looks like in VS Code:
Update the Register Block Type Action
With the block moved to a new directory you now need to update the reference to its location in the register_block_type
action:
- Edit the
wp-multi-block.php
file - Let’s give the existing function a better name and use
multiblock_register_static_blocks
- In the
register_block_type
function you need to include the new path to our block so that it looks like/build/blocks/block-one
Our final code looks like this:
function multiblock_register_blocks() {
register_block_type( __DIR__ . '/build/blocks/block-one' );
}
add_action( 'init', 'multiblock_register_blocks' );
Update the Build Files
- It’s time to regenerate the build files by running
npm run build
Let’s confirm that these changes work, activate the plugin and refresh the editor, and add our first block. We should end up with something that looks like this:
Adding additional blocks
Adding more blocks is as easy as running the same command as you did when creating the first block, but with a different name. Go ahead and create Block Two.
Create a new block
You can repeat what you did to create the first block by running the following inside the src/blocks
directory:
npx @wordpress/create-block@latest block-two --no-plugin
Here’s what my updated directory structure looks like in VS Code:
Update Register Block Type Action
- Edit the
wp-multi-block.php
file and duplicate theregister_block_type
function and change the reference to/build/blocks/block-two
.
The final code looks like this:
function multiblock_register_blocks() {
register_block_type( __DIR__ . '/build/blocks/block-one' );
register_block_type( __DIR__ . '/build/blocks/block-two' );
}
add_action( 'init', 'multiblock_register_blocks' );
Update the Build Files
- It’s time to regenerate our build files by running
npm run build
Let’s confirm that these changes work and refresh the editor and add both of our blocks. We should end up with something that looks like this:
Adding a Dynamic Block
So far, you only worked with static blocks, but how does this apply to a dynamic block? With a few updates, you can also include dynamic blocks along with our static blocks. If you aren’t familiar with both block types you can read here on the Developer Blog Static vs. dynamic blocks: What’s the difference?
You’ll start by creating a new block just like we did before, but this time we’ll add an option to specific that we are creating a dynamic block. Go ahead and create Block Three.
Create a Dynamic Block
You can once again reach for @wordpress/create-block
to create our dynamic block. Inside the src/blocks
directory and run the following:
npx @wordpress/create-block@latest block-three --no-plugin --variant dynamic
Update Register Block Type Action
- Edit the
wp-multi-block.php
file and duplicate theregister_block_type
function and change the reference to/build/blocks/block-three
.
The final code looks like this:
function multiblock_register_blocks() {
register_block_type( __DIR__ . '/build/blocks/block-one' );
register_block_type( __DIR__ . '/build/blocks/block-two' );
register_block_type( __DIR__ . '/build/blocks/block-three' );
}
add_action( 'init', 'multiblock_register_blocks' );
Update Build and Start Commands
Before you can update your build you need to add an argument into the build commands. By adding --webpack-copy-php
you are telling the build process to include any PHP file into the final build directory.
- Edit
package.json
- Update the
build
andstart
commands to tell webpack to copy the PHP files.
The final commands should look like the following:
"build": "wp-scripts build --webpack-copy-php"
"start": "wp-scripts start --webpack-copy-php"
Update the Build Files
It’s time to regenerate our build files by running npm run build
Let’s confirm that these changes work and refresh the editor and add all three of our blocks. We should end up with something that looks like this:
Note about text domain
When using @wordpress/create-block
the text-domain
in the block.json
matches the block name. In your plugin, each block will have a unique text-domain
. This would be a great time to edit each block.json
file and change the text-domain
to wp-multi-block
.
Recap of Basic Setup
Congratulations! If you’ve followed along, you’ve just created a multi-block plugin that loads three blocks and is maintained with a single set of dependencies. And you can repeat the steps to add new blocks as many times as you wish. The key points to remember are:
- Individual block names must be unique so don’t forget to update references to
names
andtitles
throughout the block files - Each block must be registered with a
register_block_type
function
Each dynamic block must have a reference to a PHP file as part of its register_block_type
function which is defined in the block.json
file
Advanced Configuration
While each section from this point forward is completely optional as you already have a fully functional multi block, I would recommend going through these additional steps to improve the overall configuration of the plugin to better streamline development, simplify maintenance and improve the overall readability of the code.
By the end of this section of the tutorial you’ll have:
- Added another dynamic block
- Created an array of blocks
- Updated functions to use a
foreach
- Added webpack to compile our assets into a single resource
- Added a script to modify a core block style.
Adding a new register_block_type
function is pretty easy, but what does that code look like repeated 20 times or more? Instead of that you can add an array of blocks, then use a foreach
to register each block in the array.
Adding Another Dynamic Block
Before we dive in it will be helpful to have a second dynamic block for testing purposes, so create one inside the src/blocks
directory:
npx @wordpress/create-block@latest block-four --no-plugin --variant dynamic
When done creating block-four you’ll need to build again by running npm run build
. This new block won’t be available in the editor just yet, we have to update a few functions first.
Create an Array and Add foreach
Loop
Start by creating an array that allows you to quickly and easily add new blocks to our plugin as needed without having to ever add another register_block_type
function. Then use a foreach
to loop through the array and register each block in the register_block_type
function. Edit the wp-multi-block.php
file and update the multiblock_register_blocks
function to look like this:
function multiblock_register_blocks() {
$custom_blocks = array (
'block-one',
'block-two',
'block-three',
'block-four',
);
foreach ( $custom_blocks as $block ) {
register_block_type( __DIR__ . '/build/blocks/' . $block );
}
}
add_action( 'init', 'multiblock_register_blocks' );
Confirm that these changes work by refreshing the editor or the frontend and adding all four of the blocks. We should end up with something that looks like this:
Now when you add new blocks all that is needed is a quick addition to the array of blocks, which keeps the rest of our code slim and much easier to maintain.
Combine Block Assets
By default, the loading of script files is handled in the block.json
file. That works well for single block plugins, in a multi-block plugin you might consider streamline loading script files.
Now, there are some pros and cons to this approach, it might not make sense for everyone. On one hand, you reduce the number of loaded items and you have more control over the output. You also lose a nice feature like loading a view.js
file only when the block is present on the page.
For the purpose of this tutorial, the preferred approach is to combine and compile our JavaScript and CSS files with the idea that you will gZip and host them on a content delivery network (CDN) in the future. To set this up you will add webpack, leverage the core WordPress default webpack config and package this all up into a single script and stylesheet.
Create a webpack Config File
First create a webpack config file. Here you will spread in the webpack config from the @wordpress/scripts
package as you want to maintain that functionality while adding your own.
Start by creating a webpack.config.js
file in the root folder of your plugin and paste in the following:
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
const path = require( 'path' );
module.exports = {
...defaultConfig,
entry: {
'multi-block-editor': [ path.resolve( __dirname, 'src/multi-block-editor.js' ) ],
'multi-block-frontend': [ path.resolve( __dirname, 'src/multi-block-frontend.js' ) ],
},
output: {
path: path.resolve( __dirname, 'build' ), filename: '[name].js',
},
};
In the webpack file you’re spreading in the config file available as part of the @wordpress/scripts
package and then add your own entries that are used to compile and load into the editor and the other to compile assets that will load on the frontend. We’ll give these entry points the names multi-block-editor and multi-block-frontend.
Add Editor and Frontend Entry Points
Now you need to create our entry point files that you specified in the webpack.config.js
file.
First add the script for the block editor. In the src
directory create a file called multi-block-editor.js
, and paste in the following:
import './blocks/block-one';
import './blocks/block-two';
import './blocks/block-three';
import './blocks/block-four';
Next add the script for the frontend assets. In the src
directory create a file called multi-block-frontend.js
, and for now simply paste in the following:
console.log( 'frontend test' )
Enqueue the editor and frontend assets
Enqueue Editor Assets
Now that you have the script and style files being output as single files you need to enqueue them into the editor using the enqueue_block_editor_assets
action.
Open up the wp-multi-block.php
file and add the following function:
function multiblock_enqueue_block_assets() {
wp_enqueue_script(
'multi-block-editor-js',
plugin_dir_url( __FILE__ ) . 'build/multi-block-editor.js',
array('wp-blocks', 'wp-components', 'wp-data', 'wp-dom-ready', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins'),
null,
false
);
wp_enqueue_style(
'multi-block-editor-css',
plugin_dir_url( __FILE__ ) . 'build/multi-block-editor.css',
array(),
null
);
}
add_action( 'enqueue_block_editor_assets', 'multiblock_enqueue_block_assets' );
Enqueue Frontend Assets
Now you need to enqueue the frontend assets by adding another function to the wp-multi-block.php
file:
function multiblock_enqueue_frontend_assets() {
wp_enqueue_style(
'multi-block-frontend-css',
plugin_dir_url( __FILE__ ) . 'build/style-multi-block-editor.css',
);
wp_enqueue_script(
'multi-block-frontend-js',
plugin_dir_url( __FILE__ ) . 'build/multi-block-frontend.js',
array(),
null,
true
);
}
add_action( 'wp_enqueue_scripts', 'multiblock_enqueue_frontend_assets' );
Updating Block Styles
In this configuration, each block has a separate file for editor and frontend styles. In most cases, what we are loading on the frontend is also what we want to load in the editor. What I prefer to do here is import my style.scss
file into the editor.scss
file. This way you can maintain one set of frontend classes while adding anything unique to the editor.
Here’s an example of how such an editor.scss
file would look like for block-one after a couple of updates. The border is only added in the editor, and by adding .is-selected
the border only appears when a block-one is selected.
Update each of the editor.scss
files for each of our blocks to import the style.scss
file like this example:
@import "./style.scss";
.wp-block-example-block-one.is-selected {
border: 2px dotted #f00;
}
Removing Unused Files and Clean Up block.json
Now that you have single file assets in place we can go through our blocks and delete any view.js files not being used, these are no longer loaded via the block. If you are adding blocks to your own multi-block plugin and have blocks that require this functionality you can include those in our frontend entry point js file. To learn how the view.js file plays a role in using the Interactivity API, you can read A first look at the Interactivity API on this site.
In each of the blocks, edit the block.json
files to remove references to the js and css files. You are looking to remove the following items:
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js"
Note: in the dynamic blocks make sure you do not remove the render line that looks like this:
"render": "file:./render.php"
Go ahead and run npm run build
to generate new plugin assets and go test the new block in the editor as well as the frontend of the development environment.
Additional Scripts
One final thing that you will include in your plugin is an example of how to add additional scripts to the compiled editor script. These scripts can be used for block modifications or variations, slot fills, or other functionality.
Add a simple script to remove the outline style on a core button.
Add a Script to Modify a Block Style
First you need to install a couple of new dependency:
npm install @wordpress/blocks @wordpress/dom-ready --save-dev
Once the package is installed create a script file like the following:
- Create a scripts directory at
src/scripts/modifications
- Create a file named
button.js
and paste the following:
import { unregisterBlockStyle } from '@wordpress/blocks';
import domReady from '@wordpress/dom-ready';
domReady(() => {
unregisterBlockStyle( 'core/button', array( 'outline' ) )
});
Now you need to include this script into the block editor entry point:
- Edit
multi-block-editor.js
- Import the
button.js
so your revised file looks like:
// Import the plugin blocks
import './blocks/block-one';
import './blocks/block-two';
import './blocks/block-three';
import './blocks/block-four';
// Import other block scripts
import './scripts/modifications/button';
Now all you need to do is update the build files with npm run build
and refresh the editor. Try adding a button you’ll see the outline
style option is now gone.
If you are interested in more ways to curate the editor experience, the post 15 ways to curate the WordPress editing experience offers additional code snippets to streamline content creation, ensure consistency, and create personalized editing experiences.
Wrapping Up
Author’s note: I hope others out there find benefit in this approach to building a block plugin in some of their future work. I’m sure there are several ways that my approach can be approved upon, and I would love to hear how we can do that as a community.
For an tutorial with a different approach, see the article Setting up a multi-block plugin using InnerBlocks and post meta.
Resources
Documentation
Developer Hours
- Modern WordPress development with the wp-scripts package
- Hello, Blocks! – An Introduction to Block Development
- Do you really need a custom block? Let’s explore alternatives
Courses on Learn.WordPress
- Introduction to Block Development: Build your first custom block
- Developing your first WordPress block
- Converting a Shortcode to a Block
Props @bph, @milana_cap and @flexseth for the review, feedback and additional resources.
Leave a Reply