Introduction
The ‘Knighttower’ block is designed to offer users unparalleled flexibility and control over content presentation. Dive into this tutorial to craft a feature-rich WordPress block, complete with customization options.
Step 1: Initial Setup & Folder Structure
- Make sure you have the required setup: https://developer.wordpress.org/block-editor/getting-started/devenv/
- Create a new directory named
Knighttower
(or whatever name you will use) inside your WordPress theme or plugin directory. Alternatively, just run the Wp command (recommended for quick boilerplate, more info here):npx @wordpress/create-block Knighttower
- Inside the
directory, create the following subdirectories:Knighttower
src
(will contain our source files for the block)build
(will contain compiled files)
Knighttower/
|-- package.json
|-- Knighttower.php
|-- build/
|-- src/
|-- style.scss
|-- editor.scss
|-- block.json
|-- edit.js
|-- index.js
|-- save.js
Step 2 – block.json: Block Metadata
Purpose: The metadata acts as the DNA of your block. It provides WordPress with crucial details about the block’s properties and functionalities, ensuring that it integrates seamlessly into the editor.
Approach:
- Use this file to define the block’s core properties.
- Define essential properties like name and category.
- Declare custom attributes to allow user customization.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "custom-block/knighttower",
"version": "0.1.0",
"title": "Knightotwer / Custom Content",
"category": "widgets",
"icon": "smiley",
"description": "Example block scaffolded with Create Block tool.",
"example": {},
"attributes": {
"width": {
"type": "string",
"default": "100%"
},
"padding": {
"type": "string"
},
"margin": {
"type": "string"
},
"minimumHeight": {
"type": "string"
}
},
"supports": {
"spacing": {
"margin": true,
"padding": true
},
"dimensions": {
"width": true,
"minimumHeight": true
},
"html": false
},
"textdomain": "knighttower",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
Step 3 – style.css & editor.css: Block Styling
Ensure that your block has a consistent appearance on both the frontend and the editor.
style.scss (or css if not using sass)
/**
* The following styles get applied both on the front of your site
* and in the editor.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-custom-block-knighttower {
background-color: #21759b;
color: #fff;
padding: 2px;
}
editor.scss (or css if not using sass)
/**
* The following styles get applied inside the editor only.
*
* Replace them with your own styles or remove the file completely.
*/
.wp-block-custom-block-knighttower {
border: 2px dotted #f00;
}
step 4 – edit.js: The Block’s Editing Experience
Purpose: This is where you shape the user’s interaction with the block in the editor. By defining UI components, toolbar settings, and nested block capabilities, you offer a dynamic editing experience.
Approach:
- Implement UI components for custom attributes.
- Use components like
InnerBlocks
for nested functionality. - Provide toolbar settings for customization.
Note: Out block supports “padding” and “margin”, nested blocks, but it also has a “width” and “height”
/**
* 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';
/**
* 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 {
InnerBlocks,
BlockControls,
BlockVerticalAlignmentToolbar,
AlignmentToolbar,
InspectorControls,
useBlockProps,
store as blockEditorStore
} from '@wordpress/block-editor';
import {
__experimentalUseCustomUnits as useCustomUnits,
PanelBody,
__experimentalUnitControl as UnitControl
} from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
/**
* 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 {WPElement} Element to render.
*/
function Edit({ attributes, setAttributes }) {
const { width, verticalAlignment, align, paddingTopBottom, paddingLeftRight, marginTopBottom, marginLeftRight, minimumHeight } = attributes;
// Define available units.
const units = useCustomUnits({
availableUnits: ['%', 'px', 'em', 'rem', 'vw']
});
const { updateBlockAttributes } = useDispatch(blockEditorStore);
// Define styles and classes for the block.
const blockProps = useBlockProps({
style: {
width: width,
padding: `${paddingTopBottom} ${paddingLeftRight}`,
margin: `${marginTopBottom} ${marginLeftRight}`,
minHeight: minimumHeight
}
});
// ... (rest of the imports and beginning of the Edit function)
return (
<>
<BlockControls>
<AlignmentToolbar
value={align}
onChange={(newAlign) => setAttributes({ align: newAlign })}
/>
<BlockVerticalAlignmentToolbar
onChange={(value) => {
setAttributes({ verticalAlignment: value });
}}
value={verticalAlignment}
/>
</BlockControls>
<InspectorControls>
<PanelBody title={__("Knighttower Block Settings")}>
<UnitControl
label={__("Width")}
labelPosition="edge"
__unstableInputWidth="80px"
value={width || ''}
onChange={(nextWidth) => {
setAttributes({ width: nextWidth });
}}
units={units}
/>
<UnitControl
label={__("Minimum Height")}
labelPosition="edge"
__unstableInputWidth="80px"
value={minimumHeight || ''}
onChange={(nextMinHeight) => {
setAttributes({ minimumHeight: nextMinHeight });
}}
units={units}
/>
{/* ... (add other controls here as needed) */}
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<InnerBlocks />
</div>
</>
);
}
export default Edit;
step 5 – index.js: Block Registration
Purpose: This acts as your block’s passport, introducing it to WordPress. By registering the block, you ensure that WordPress recognizes and accepts your custom block code.
Approach:
- Import required functions and components.
- Register the block with its attributes and functionalities.
/**
* 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,
} );
step 6 – save.js: Block’s Output
Purpose: This is your block’s public face. It defines how the block appears on the website after being edited in WordPress. Ensuring a consistent appearance between the editor and frontend is crucial for user trust.
/**
* 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, InnerBlocks } from '@wordpress/block-editor';
/**
* The save function defines the way in which the different attributes should
* be combined into the final markup, which is then serialized by the block
* editor into `post_content`.
*
* @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-edit-save/#save
*
* @return {WPElement} Element to render.
*/
export default function Save( { attributes } ) {
const styles = {
width: attributes.width,
padding: `${attributes.paddingTopBottom} ${attributes.paddingLeftRight}`,
margin: `${attributes.marginTopBottom} ${attributes.marginLeftRight}`,
minHeight: attributes.minimumHeight
};
return (
<div { ...useBlockProps.save() } style={styles}>
<InnerBlocks.Content />
</div>
);
}
step 7 – Custom Attributes
Purpose: Attributes act as adjustable settings for your block. By defining custom attributes, you offer users the power to tweak the block’s appearance and behavior without altering its core code.
Approach:
- Use the block metadata (
block.json
) to define custom attributes. - Implement UI components in
edit.js
for users to adjust these attributes. For instance, a slider could be used for width adjustment, while dropdowns or input boxes might be suitable for padding and margin. - Utilize these attributes in the block’s editing (
edit.js
) and saving (save.js
) functionalities to dynamically adjust the block’s appearance based on user input.
step 8 – Toolbar Settings
Purpose: Toolbars are user’s toolkits. They offer handy customization options right at the user’s fingertips, ensuring a smooth editing experience.
Approach:
- Integrate alignment options for content positioning within the block.
- Add a lock button to prevent accidental edits or changes.
<BlockControls>
<AlignmentToolbar value={align} onChange={(newAlign) => setAttributes({ align: newAlign })} />
<BlockVerticalAlignmentToolbar
onChange={(value) => {
setAttributes({ verticalAlignment: value });
}}
value={verticalAlignment}
/>
</BlockControls>
step 9 – Nested Blocks
Purpose: Nested blocks elevate your block from a static element to a dynamic container. They allow users to insert additional blocks within yours, leading to richer content structures.
Approach:
- Use the
<InnerBlocks />
component, allowing users to add other blocks inside the ‘Knighttower’ block.
<div {...blockProps}>
<InnerBlocks />
</div>
step 10 – Knighttower.php
Purpose: This PHP script is the bridge between your block’s frontend styles, its editor scripts, and WordPress. It registers the block, ensuring WordPress can utilize it.
Approach:
- Use the
register_block_type
function to tell WordPress about your block and link it to the necessary styles and scripts.
<?php
/**
* Plugin Name: Knighttower / Custom Block
* Description: Example block scaffolded with Create Block tool.
* Requires at least: 6.1
* Requires PHP: 7.0
* Version: 0.1.0
* Author: The WordPress Contributors
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: knighttower
*
* @package custom-block
*/
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function knighttowerBlockInit()
{
register_block_type(__DIR__ . '/build');
}
add_action('init', 'knighttowerBlockInit');
Final Step: Code Overview
package.json
{
"name": "knighttower",
"version": "0.1.0",
"description": "Example block scaffolded with Create Block tool.",
"author": "The WordPress Contributors",
"license": "GPL-2.0-or-later",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts 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",
"start": "wp-scripts start"
},
"devDependencies": {
"@wordpress/scripts": "^26.10.0"
}
}
Run “npm run build” to compile the block, that’s it!
Sources:
- WordPress create blocks:
- WP Block API:
Let us know your opinion!