What are Gutenberg Blocks?
Gutenberg is the modern editor for WordPress, using “blocks” as the fundamental unit of content. Each block is an independent content element:
- Paragraphs, headings, lists
- Images, videos, audio
- Buttons, columns, groups
- Custom blocks
Development Environment Setup
Required Tools
# Node.js 18+
node -v
# npm or yarn
npm -v
# WordPress Local Environment
# Recommended: Local by Flywheel, wp-env, Docker
Creating a Block Plugin
# Use the official scaffolding tool
npx @wordpress/create-block my-first-block
# Enter the directory
cd my-first-block
# Start development
npm start
Block Structure
File Organization
my-first-block/
├── build/ # Compiled output
├── src/
│ ├── block.json # Block metadata
│ ├── edit.js # Editor component
│ ├── save.js # Frontend output
│ ├── index.js # Entry file
│ ├── editor.scss # Editor styles
│ └── style.scss # Frontend styles
├── my-first-block.php # Main plugin file
└── package.json
block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "my-plugin/my-first-block",
"version": "1.0.0",
"title": "My First Block",
"category": "widgets",
"icon": "smiley",
"description": "A simple block example",
"supports": {
"html": false
},
"textdomain": "my-first-block",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
Writing Block Code
edit.js - Editor Component
import { useBlockProps, RichText } from "@wordpress/block-editor";
import { __ } from "@wordpress/i18n";
export default function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<RichText
tagName="p"
value={attributes.content}
onChange={(content) => setAttributes({ content })}
placeholder={__("Enter text...", "my-first-block")}
/>
</div>
);
}
save.js - Frontend Output
import { useBlockProps, RichText } from "@wordpress/block-editor";
export default function Save({ attributes }) {
const blockProps = useBlockProps.save();
return (
<div {...blockProps}>
<RichText.Content tagName="p" value={attributes.content} />
</div>
);
}
index.js - Registering the Block
import { registerBlockType } from "@wordpress/blocks";
import Edit from "./edit";
import Save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
edit: Edit,
save: Save,
});
Block Attributes
Defining Attributes
// block.json
{
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p"
},
"alignment": {
"type": "string",
"default": "left"
},
"backgroundColor": {
"type": "string"
}
}
}
Using Attributes
// edit.js
const { content, alignment, backgroundColor } = attributes;
setAttributes({ alignment: "center" });
Common Components
Toolbar Controls
import { BlockControls, AlignmentToolbar } from "@wordpress/block-editor";
<BlockControls>
<AlignmentToolbar
value={alignment}
onChange={(newAlign) => setAttributes({ alignment: newAlign })}
/>
</BlockControls>
Sidebar Settings
import { InspectorControls } from "@wordpress/block-editor";
import { PanelBody, ColorPicker } from "@wordpress/components";
<InspectorControls>
<PanelBody title={__("Settings", "my-block")}>
<ColorPicker
color={backgroundColor}
onChange={(color) => setAttributes({ backgroundColor: color })}
/>
</PanelBody>
</InspectorControls>
Dynamic Blocks
Server-side rendered blocks:
// PHP registration
register_block_type(__DIR__ . "/build", [
"render_callback" => "render_my_dynamic_block"
]);
function render_my_dynamic_block($attributes) {
$recent_posts = wp_get_recent_posts([
"numberposts" => 5,
"post_status" => "publish"
]);
$output = "<ul>";
foreach ($recent_posts as $post) {
$output .= "<li>" . esc_html($post["post_title"]) . "</li>";
}
$output .= "</ul>";
return $output;
}
// save.js returns null
export default function Save() {
return null;
}
Debugging Tips
Enabling Development Mode
// wp-config.php
define("SCRIPT_DEBUG", true);
define("WP_DEBUG", true);
Browser Tools
- React DevTools
- Redux DevTools (for viewing block state)
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Block validation failed | save output doesn’t match stored markup | Check save.js |
| Cannot read property | property undefined | Set default values |
| Invalid block | block.json format error | Validate JSON |
Learning Resources
For block development issues, you can ask the @gutenberg expert for assistance!