Block Editor

The WordPress Block Editor (Gutenberg) is a modern editing experience that allows content to be created using modular components called blocks. Each block represents a piece of content such as text, images, lists, or custom UI elements.

Creating custom blocks allows developers to extend the editor with tailored functionality suited to specific websites or applications.

The process of learning block development typically begins with a simple example and progressively adds functionality such as attributes, controls, styling, and dynamic rendering.

The objective of this lesson is to introduce the fundamentals of creating a custom Gutenberg block. The learning process follows a gradual approach:

  • Start with the simplest possible block.

  • Incrementally add common functionality.

  • Understand how block assets and structure work.

  • Learn how JavaScript and PHP interact during block registration.

Developers are encouraged to experiment by modifying code examples and observing how these changes affect the block’s behavior.


Code Syntax Formats

Two formats are typically used when writing Gutenberg block code.

JSX

JSX is a syntax extension for JavaScript used with frameworks like React.

Characteristics:

  • Requires a build process

  • Compiled into browser-compatible JavaScript

  • Easier to read and write

  • Widely used in Gutenberg development

Example JSX:

return <p>Hello world!</p>;

Plain JavaScript

Plain JavaScript does not require a build step.

Characteristics:

  • Compatible with browsers without compilation

  • Slightly more verbose syntax

  • Useful for simpler setups

Example Plain JavaScript:

return wp.element.createElement(
    "p",
    null,
    "Hello world!"
);

Although both approaches work, most modern Gutenberg development uses JSX with a build step.


Creating a Gutenberg Block

Creating a block involves a combination of JavaScript, Node.js build tooling, and WordPress PHP registration.


Step 1: Create a Plugin

Blocks are usually distributed through WordPress plugins.

Example plugin structure:

my-first-block-plugin
│
├── my-first-block-plugin.php
├── package.json
├── src
│   └── first-block
│       ├── block.json
│       └── index.js
└── build

Step 2: Initialize Node.js

Inside the plugin folder, initialize a Node.js project.

npm init

This generates a package.json file used to manage dependencies and scripts.


Step 3: Install WordPress Scripts

Install the official WordPress development tools.

npm install @wordpress/scripts --save-dev

The @wordpress/scripts package provides build tools such as:

  • Webpack

  • Babel

  • ESLint

  • Preconfigured Gutenberg environment


Step 4: Configure Build Scripts

Add the following scripts to package.json.

"scripts": {
  "start": "wp-scripts start",
  "build": "wp-scripts build"
}

Explanation:

ScriptPurpose
startStarts development build with live reloading
buildGenerates optimized production files

Step 5: Create Block Directory

Inside the plugin folder create the following structure.

src/
   first-block/

This directory will contain the block source files.


Step 6: Create block.json

The block.json file defines the block’s metadata and configuration.

Example:

{
	"$schema": "https://schemas.wp.org/trunk/block.json",
	"apiVersion": 3,
	"title": "First Block",
	"name": "learning/first-block",
	"version": "0.1.0",
	"category": "text",
	"icon": "universal-access-alt",
	"textdomain": "first-block",
	"editorScript": "file:./index.js"
}

Explanation of key fields:

FieldDescription
apiVersionSpecifies the block API version
titleBlock name shown in the editor
nameUnique block identifier
categoryEditor category where the block appears
iconDashicon used in the block inserter
textdomainUsed for translation
editorScriptJavaScript file used in the editor

The block.json file allows WordPress to automatically register assets and configuration.


Step 7: Create index.js

The index.js file contains the JavaScript logic that defines the block.

Example:

import { registerBlockType } from "@wordpress/blocks";
import metadata from "./block.json";

// Register the block
registerBlockType(metadata.name, {
	edit: function () {
		return <p>Hello world! This is my first block. (editor)</p>;
	},
	save: function () {
		return <p>Hello World! This is my first block. (frontend)</p>;
	},
});

Explanation:

FunctionPurpose
registerBlockTypeRegisters the block in Gutenberg
editDefines how the block appears inside the editor
saveDefines how the block is stored and displayed on the frontend

Important concept:

  • edit() runs inside the editor.

  • save() defines the HTML saved into the post content.


Step 8: Register Block in PHP

Finally, register the block on the server side inside the plugin’s main PHP file.

function register_blocks() {
	register_block_type( __DIR__ . '/build/first-block' );
}

add_action( 'init', 'register_blocks' );

Explanation:

FunctionPurpose
register_block_type()Registers the compiled block files
add_action(‘init’)Ensures registration during WordPress initialization

The build folder contains compiled assets produced by the JavaScript build process.


Step 9: Build the Block

Run the build command.

npm run build

This command:

  • Compiles JSX into browser-compatible JavaScript

  • Bundles assets

  • Generates the build directory

Once built, the block will appear inside the WordPress block inserter.


JavaScript and PHP Registration

Blocks are registered in both:

  • JavaScript → for the editor interface

  • PHP → for WordPress server-side awareness

This dual registration ensures:

  • Proper asset loading

  • Compatibility with server-side rendering

  • Block availability in REST API and frontend


Asset Handling

When using block.json, WordPress automatically handles:

  • Script enqueuing

  • Style loading

  • Translations

  • Dependencies

This reduces manual configuration for developers.


Build Assets File

After building, an asset file is generated automatically.

Example:

index.asset.php

This file contains:

  • Script dependencies

  • Version information

Example structure:

return array(
    'dependencies' => array('wp-blocks', 'wp-element'),
    'version' => '123456'
);

WordPress uses this file to load scripts correctly.


Block Styles

Custom block styles can be registered using:

registerBlockStyle('core/paragraph', {
    name: 'xyz',
    label: 'XYZ Style'
});

When applied, Gutenberg adds the CSS class:

is-style-xyz

to the block wrapper.


Client-Side Only Blocks

Technically, blocks can be registered only in JavaScript without PHP.

However, server-side registration is recommended because it:

  • Ensures asset loading

  • Improves compatibility

  • Supports server-rendered blocks

  • Allows block discovery by WordPress