← All Articles A Product of Kinsa Creative

Creating Custom Gutenberg Blocks for WordPress

Create a plugin to house the code for the custom Gutenberg blocks

To create a new plugin, create a directory for it: in the wp-content/plugins directory, create a new directory for the plugin e.g. my-custom-blocks. As a convention, this name should be snake case and will be referenced throughout as a namespace for the blocks.

Within that directory, make a new file by the same name e.g. my-custom-blocks.php.

Add to that a minimum of the following:

<?php
/*
Plugin Name: My Custom Blocks
Description: This plugin adds my custom blocks.
Author: First Last
*/

Create a custom Gutenberg block

Dynamic Blocks

Dynamic blocks are useful if you want to add a block that outputs the contents of ACF fields or some arbitrary PHP code. Dynamic blocks require editing a render.php file for output on the frontend and editing the edit.js file to define any output within the WP Admin.

To create a standard block, in the following code example, avoid the --variant="dynamic" flag.

Running the Scaffolding Script

Navigate to the plugin directory and use the WP Scaffold to create the custom block. In a shell, where my-custom-block is the name of the block and you want it to be of the dynamic type:

cd wp-content/plugins/my-custom-blocks/
npx @wordpress/create-block@latest --variant="dynamic" my-custom-block

Modifying the Structure to Allow for Multiple Custom Blocks within a Single Plugin

The scaffolding script creates a directory at the same level as my-custom-blocks/src.

Change directory into wp-content/plugins/my-custom-blocks/my-custom-block and delete everything but the src directory within it.

cd my-custom-block
find . -maxdepth 1 -not -name 'src' -not -name '.' -exec rm -rf {} +

Move the files from the src directory to the top level of the directory so they are immediate descendents of wp-content/plugins/my-custom-blocks/my-custom-block/:

mv src/my-custom-block/* ./

Delete wp-content/plugins/my-custom-blocks/my-custom-block/src:

rm -rf src

Move the new directory into a src/ directory at the root of the plugin so the path to the custom block is now something like wp-content/plugins/my-custom-blocks/src/my-custom-block:

cd ..
mkdir src
mv my-custom-block src/

Register the block in wp-content/plugins/my-custom-blocks/my-custom-blocks.php. Edit that file to include:

function my_custom_blocks_init(): void {
    register_block_type(__DIR__ . '/build/my-custom-block');
}

add_action('init', 'my_custom_blocks_init');

Add a package.json file. This replaces the package.json file we deleted from my-custom-block after creating it with the scaffolding. As a direct descendant of my-custom-blocks/, create the package.json file. Add to it:

{
    "name": "my-custom-blocks",
    "version": "0.1.0",
    "description": "My Custom Blocks",
    "author": "First Last",
    "license": "GPL-2.0-or-later",
    "main": "build/index.js",
    "scripts": {
        "build": "wp-scripts build --webpack-src-dir=src --output-path=build",
        "start": "wp-scripts start --webpack-src-dir=src --output-path=build",
        "format": "wp-scripts format",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js",
        "packages-update": "wp-scripts packages-update",
        "plugin-zip": "wp-scripts plugin-zip",
        "test:unit": "wp-scripts test-unit-js"
    },
    "devDependencies": {
        "@wordpress/scripts": "^29.0.0"
    }
}

Edit the src/my-custom-block/block.json file:

Nesting Blocks Inside of Other Blocks

To create a block that allows children, a common pattern for setting up a specific grid or flex layout or card structure, in the edit.js file of the parent block, in the returned component, add, where we are allowing only the my-custom-blocks/my-custom-block card to be added and we have included a first card in the template:

<InnerBlocks
    template={[["my-custom-blocks/my-custom-block"]]}
    allowedBlocks={["my-custom-blocks/my-custom-block"]}
/>

Modify the block.json file as well to indicate that the block supports inner blocks and which blocks to allow:

{
    ...
    "supports": {
        "innerBlocks": true
    },
    "allowedBlocks": [
        "core/paragraph",
        "core/spacer",
        "my-custom-blocks/my-custom-block"
    ],
    ...
}

To allow any child blocks, the edit.js file simply needs to return the InnerBlocks component:

import { InnerBlocks, useBlockProps } from "@wordpress/block-editor";
import "./editor.scss";

export default function Edit() {
    return (
        
<InnerBlocks />
); }

Modify the block.json file as well to indicate that the block supports inner blocks and omit defining allowedBlocks which will allow all blocks by default:

{
    ...
    "supports": {
        "innerBlocks": true
    },
    ...
}

The save.js file in either case returns the content of the inner blocks:

import { InnerBlocks, useBlockProps } from "@wordpress/block-editor";

export default function save() {
    return (
        
<InnerBlocks.Content />
); }

Restricting a Block to Be a Child of a Certain Other Type of Block

To limit a block so it can only be used in a parent block, in the block.json file of the child block, add a key for parent and set the value to an array of parent blocks (in this example my-custom-blocks/my-custom-block-wrapper), e.g.:

"parent": ["my-custom-blocks/my-custom-block-wrapper"],

Building

The wp-content/plugins/my-custom-blocks/package.json file lists the scripts available. During development, start will compile the blocks on demand while build will do a one-time build. To run the scripts, from the wp-content/plugins/my-custom-blocks/ directory, run:

npm run start

or

npm run build

Adding a Combination of Blocks to the Plugin as a Pattern

Often when theming a website with Gutenberg custom blocks, a combination of blocks will be used to create an effect. Collecting the group as a pattern provides a useful way for content creators to find and add them.

To do this, assemble the combination of Gutenberg blocks on a new post. Select the whole combination of blocks and right-click to create a new pattern. You probably don't want it to be global unless the blocks are all dynamic. With global patterns, a change to one use affects all the others. Save the combination and then navigate to the theme → patterns and export the JSON.

Open the JSON in a text editor, copy out the value of content and append it to wp-content/plugins/my-custom-blocks/my-custom-blocks.php, wrapping it in a function and hooking that function like so:

function my_custom_blocks_patterns_init(): void
{
    register_block_pattern('my-custom-blocks/my-pattern', array(
            'title' => __('My Pattern', 'my-custom-blocks'),
            'categories' => array('featured'), // adding the pattern to the featured collection makes it easier for content editors to find
            'source' => 'plugin',
            'content' => ""
    ));
}
add_action('init', 'my_custom_blocks_patterns_init');

Feedback?

Email us at enquiries@kinsa.cc.