Beginner Guide To Creating WordPress Block Plugins
Beginner Guide to creating WordPress block plugins to efficiently build creative UI addons enhancing your website.

In this post I will show you how I was able to start creating WordPress block plugins.

I am still in the beginning stages of learning this myself. As I go through the documentation and develop working examples, I will post the examples with problematic issues I faced that could help others. There is a lot of documentation out there but it isn't very clear in some cases. Good information is also available on Stackoverflow but they seem to be more advanced issues with established templates.

I started my attempt to create a very simple block plugin for another website. It was suppose to be a quick day project that easily turned into a few days due to my specific struggles in going from beginner coding attempts to a more advanced attempt. I quickly had to rewind to learn the basics before I could move forward. This is what the post below is about. I want to inform about the basics and how WordPress handles the edited data when creating blocks in your posts. Some parts I skimmed over are covered well in the WordPress development documentation and I focus on the parts that I struggled with.

I am demonstrating this in a development environment on a Windows machine with WordPress installed in IIS. The actual project example is far from a complete block plugin. I am posting this because I have just been able to differentiate the necessary components needed between the two types of rendering. I wanted to share while it was fresh.

Creating WordPress Block Plugins

Use the link below to view a guide I created on how to install WordPress on IIS.

Types of Block Rendering

WordPress has two main types of block rendering. There are treated differently based on the supplied data.

Static

Static rendering is when you supply a data set used to create the front end markup for the site elements. These are attributes that are created when building the block plugin. The data in the attributes can change but the attributes types can not change. The data is not dynamically created from outside sources. In the example I post below, I am changing the text of a paragraph element.

Dynamic

Dynamic render is used when you have a potentially ever changing set of data used to create the front end markup for the site elements. An example would be getting the data from the last 5 posts to use in your plugin. As you publish more posts, the last 5 posts could change. In the example I post below, I use an empty array. When the user builds their post with the custom block plugin, they can edit it. The dynamic part is that the array could change in size, 1 element or 15 elements each with different values. 

Setup

You will need to setup Node.js development tools on your system in order to create WordPress block plugins.

I will post two links below that you can use as a guide to install the Node.js developments tool.

Create and Setup React App For Android - this is a previous post of mine. The beginning steps are the important steps for this guide. They show you how to add React.js to your system.

Once your system is setup to work with Node.js, we can now start creating WordPress block plugins.

WordPress Tutorial: Build your first block should be your starting steps. They walk you through the beginning parts well. I want to add in the information that doesn't seem clear and took me some time to figure out. This guide will have you import a sample project template to use as a foundation to creating WordPress block plugins.

I am starting after you import the template project they specify from the link above to use with the following command.

Navigate to your WordPress plugins folder. Here you have two options.

Option #1

You can have a guided build with options that populate for you to customize by executing:

npx @wordpress/create-block@latest

Option #2

Or you can have a project template created with the project name of Copyright Date Block built by executing:

npx @wordpress/create-block@latest copyright-date-block --variant=dynamic
cd copyright-date-block

Open a code editor in the src/<plugin_name> directory to display the necessary files needed to edit.

IMPORTANT

Do not forget to start NPM development in this plugin main folder so all the appropriate files are updated automatically for you. Also don't forget to activate the plugin in the Plugin Menu via the WordPress Admin Dashboard.
npm run start

Static Rendering Block Plugin

block.json

The attributes and the support parts are where we will spend some time. The rest is explained well enough in the WordPress Developer Documentation site.

Attributes

The attributes value is an array of values used to create/build the block plugin. These values could be what is used on the front end for the user to change, such as a title for something or padding for DOM elements created while customizing the plugin. Attributes for each item will need to have a few things specified.

type

the type of attribute this is. A text, number, array, etc.

selector

The selector is what type of DOM element this will be. A p, span, div, etc. You can also differentiate multiple p elements with p.differentName.

Support

The support array holds specific values you want to update the DOM object with. These are used in the Settings pane for styling the post data of the block plugin. The sample code below enables the ability to change the color to the background and text, enable "Edit as HTML" on block and change text typography.
{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/copyright-date-block",
	"version": "0.1.0",
	"title": "Copyright Date Block",
	"category": "widgets",
	"description": "My new description here...",
	"example": {},
	"attributes": {
		"rowCount": {
			"type": "number",
			"default": 2
		},
		"staticElement": {
			"type": "string",
			"default":"",
			"selector":"p"
		}
	},
	"supports": {
	  "color": {
		  "background": true,
		  "text": true
		},
		"html": true,
		"typography": {
			"fontSize": true
		}
	},
	"textdomain": "copyright-date-block",
	"editorScript": "file:./index.js",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"viewScript": "file:./view.js"
  }

index.js

This is mostly taken straight from the template. The only changes here are for the save functions.

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import save from './save';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,

		/**
	 * @see ./save.js
	 */
		save,
} );

Added the following two lines.

These will enable you to save data changes to your WordPress database in HTML block coding syntax.

import save from './save';
        /**
             * @see ./save.js
        */        
        save,

Save.js

This will just need to be created. In the beginning of the guide from WordPress using this template, it is not there. Later steps in the guide, they have you create it.

There are a few important notes here. The RichText utilized here will need to be RichText.Content otherwise you will get an error. The tagName has to be the same as the selector you chose in the block.json.

The structure for the Edit.js and Save.js need to be the same or the plugin will fail. WordPress checks the compiled HTML code with the code saved in the database to ensure they are the same. If they differ, it will fail to load. That is why this is considered static rendering. The Save.js has no setAttributes method to use in altering the data.

import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function save( { attributes } ) {
	const blockProps = useBlockProps.save();

    const { staticElement } = attributes;


    return (
    <>
        <div { ...blockProps }>


            <RichText.Content
            className= { "blah_" }
            tagName="p"
            value={ staticElement }
        />

        
        </div>
    </>
        );
}

Edit.js

This is where the customization and creations happens.

The InspectorControls and the PanelBody will create the Settings box in the right pane to customize attributes for the block plugin. The image below depicts what the Settings menu control will look like with the code below.

Results

Editing the block plugin.

View the post when the post content is saved.

The image below shows the database entry for the post content this block plugin was added to.

Dynamic Rendering Block Plugin

To create a dynamic block plugin, I will be using an array attribute. The array attribute will be created in edit mode with any number of text elements to be displayed as a DOM p.

Block.json

There are only a few changes here. I updated the attributes to add in an array named "content" .

On the bottom, I added in a render parameter to include a new file named render.php. This is how you will be able to display the dynamically created array of elements in WordPress.

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"name": "create-block/copyright-date-block",
	"version": "0.1.0",
	"title": "Copyright Date Block",
	"category": "widgets",
	"description": "My new description here...",
	"example": {},
	"attributes": {
		"rowCount": {
			"type": "number",
			"default": 2
		},
		"content": {
			"type": "array",
			"default":["",""],
			"selector":"p"
		},
		"staticElement": {
			"type": "string",
			"default":"",
			"selector":"p"
		}
	},
	"supports": {
	  "color": {
		"background": true,
		"text": true
		},
		"html": true,
		"typography": {
			"fontSize": true
		}
	},
	"textdomain": "copyright-date-block",
	"editorScript": "file:./index.js",
	"render": "file:./render.php",
	"editorStyle": "file:./index.css",
	"style": "file:./style-index.css",
	"viewScript": "file:./view.js"
  }

Index.js

Nothing in here has changed. We will make the necessary change in save.js.

/**
 * Registers a new block provided a unique name and an object defining its behavior.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
import { registerBlockType } from '@wordpress/blocks';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * All files containing `style` keyword are bundled together. The code used
 * gets applied both to the front of your site and to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './style.scss';

/**
 * Internal dependencies
 */
import Edit from './edit';
import save from './save';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/
 */
registerBlockType( metadata.name, {
	/**
	 * @see ./edit.js
	 */
	edit: Edit,

		/**
	 * @see ./save.js
	 */
		save,
} );

Save.js

Basically we are just returning null. This will bypass the save.js to validate and pull the saved HTML from database. WordPress will use the render.php for the front end creation.

import { __ } from '@wordpress/i18n';
import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function save( { attributes } ) {

    return null;
}

Render.php

Dynamic rendering is taken place here. This is where the HTML on the front end is created from the saved attributes in the database.

<?php
/**
 * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render
 */

 $rowCount = $attributes['rowCount'];
 $c = $attributes['content'];

?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<p><?php esc_html_e( 'Copyright Date Block – hello from a dynamic block!', 'copyright-date-block' ); ?></p>
	<?php
		foreach($c as $text){
	?>
		<p class="example">
		<?php echo esc_html( $text ); ?>
	<?php
		}
	?>
		</p>
</div>

Edit.js

This part took me awhile primarily due to the syntax rules in Javascript and how React works. I need to brush off some cob webs from my Javascript.

The flow is:

  • When the user updates the rowCount from the Settings pane, the array size updates accordingly.
  • When the user updates the text in each array element inside the RichText via the plugin on the DOM page, the value is saved at that indice.
/**
 * Retrieves the translation of text.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-i18n/
 */
import { __ } from '@wordpress/i18n';

/**
 * React hook that is used to mark the block wrapper element.
 * It provides all the necessary props like the class name.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/#useblockprops
 */

import { useBlockProps } from '@wordpress/block-editor';

/**
 * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files.
 * Those files can contain any CSS code that gets applied to the editor.
 *
 * @see https://www.npmjs.com/package/@wordpress/scripts#using-css
 */
import './editor.scss';

/**
 * The edit function describes the structure of your block in the context of the
 * editor. This represents what the editor will render when the block is used.
 *
 * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#edit
 *
 * @return {Element} Element to render.
 */

import { InspectorControls, RichText } from '@wordpress/block-editor';

import { PanelBody, TextControl } from '@wordpress/components';

import { __experimentalNumberControl as NumberControl } from '@wordpress/components';



export default function Edit( { attributes, setAttributes } ) {

	const { rowCount, content } = attributes;
	const blockProps = useBlockProps();

	function updateArray(arr, attributes){
		if(arr < 0){
		  //return if accidently set invalid number
			setAttributes({rowCount : 0});
			return;
		}
		if(arr === ""){
		  //return is no value entered
			return;
		}
		{blockProps}
		console.log(arr + " - " + attributes.rowCount);
		
		var oldArr = attributes.content;
		//check whether new input is bigger or less then do action on array
		if(arr > attributes.content.length){
			var diff = arr - attributes.content.length;
			for(let i = 0; i < parseInt(diff); i++){
				oldArr.push("");
			}
			setAttributes({ content: oldArr });
		} else if(arr < attributes.content.length) {
			var diff = attributes.content.length - arr;
			setAttributes({ content: oldArr.slice(0, -diff) });
		}
		//update the row number
		setAttributes({rowCount : arr});
	}

	function updateText(index, text, attributes){
		{blockProps}
		console.log(index + " = " + text);
		var oldArr = attributes.content;		
		oldArr[index] = text;
		setAttributes( { content: oldArr });
	}

	return (
		<>
			<InspectorControls>
				<PanelBody title={ __( 'Settings', 'copyright-date-block' ) }>
					<NumberControl
                        __next40pxDefaultSize
						spinControls='native'
						label = { __('Configure row count')}
						isShiftStepEnabled={ true }
						onChange={ ( value ) =>
                            updateArray(value, attributes) }
						shiftStep={ 1 }
						value={ parseInt(rowCount) }
					/>
                </PanelBody>
            </InspectorControls>
			
			<div { ...blockProps }>
					
						{ content.map((item, index) => {
							return (
							
							<RichText
								className= { "blah_" + index }
								tagName="p" 
								value={ item } 
								allowedFormats={ [ 'core/bold', 'core/italic' ] } 
								onChange={ ( value ) => updateText(index, value, attributes) } 
								placeholder={ __( '?...' ) } 
							/>
							
							)
								})
						}
						
					
			</div>
			
		</>
		
	);
}

Result

Editing the block plugin.

View the post when the post content is saved.

The image below shows the database entry for the post content this block plugin was added to.

Hope this was useful.