WordPress Custom Block Development - Complete Guide
Mahesh Waghmare WordPress Gutenberg blocks have revolutionized how we build WordPress sites. Creating custom blocks allows you to extend WordPress functionality and create unique content experiences. This comprehensive guide will walk you through building custom blocks from scratch.
Introduction to Custom Blocks
Gutenberg blocks are the building blocks of modern WordPress content. Custom blocks let you create reusable components that match your specific needs.
Why Create Custom Blocks?
- Reusable content components
- Consistent design patterns
- Enhanced user experience
- Better content management
- Modern development workflow
Block Types:
- Static blocks - Fixed content structure
- Dynamic blocks - Server-rendered content
- Nested blocks - Blocks within blocks
Prerequisites
Before starting, ensure you have:
- WordPress 5.0+ installed
- Node.js and npm installed
- Basic knowledge of JavaScript (ES6+)
- Understanding of React (blocks use React)
- Code editor (VS Code recommended)
- Local development environment
Required Tools:
@wordpress/scripts- Build tooling@wordpress/block-editor- Block editor components@wordpress/components- UI components
Understanding Block Structure
A custom block consists of:
1. Block Registration
- Block name and namespace
- Block configuration
- Block attributes
2. Edit Component
- What editors see in the editor
- Interactive editing interface
- Uses React components
3. Save Function
- What gets saved to the database
- HTML output for frontend
- Can be static or dynamic
4. Block Styles
- Editor styles
- Frontend styles
- Responsive design
Creating Your First Block
Step 1: Setup Project Structure
Create your block plugin structure:
my-custom-blocks/
├── package.json
├── src/
│ ├── blocks/
│ │ └── my-first-block/
│ │ ├── index.js
│ │ ├── edit.js
│ │ ├── save.js
│ │ └── style.css
│ └── index.js
└── build/
Step 2: Install Dependencies
npm install @wordpress/scripts --save-dev
npm install @wordpress/block-editor @wordpress/components --save
Step 3: Configure package.json
{
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
}
}
Step 4: Register Your Block
src/blocks/my-first-block/index.js:
import { registerBlockType } from '@wordpress/blocks';
import Edit from './edit';
import save from './save';
registerBlockType('my-plugin/my-first-block', {
title: 'My First Block',
icon: 'smiley',
category: 'common',
attributes: {
content: {
type: 'string',
source: 'html',
selector: 'p',
},
},
edit: Edit,
save,
});
Step 5: Create Edit Component
src/blocks/my-first-block/edit.js:
import { useBlockProps, RichText } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
export default function Edit({ attributes, setAttributes }) {
const { content } = attributes;
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<RichText
tagName="p"
value={content}
onChange={(value) => setAttributes({ content: value })}
placeholder={__('Enter content...', 'my-plugin')}
/>
</div>
);
}
Step 6: Create Save Function
src/blocks/my-first-block/save.js:
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save({ attributes }) {
const { content } = attributes;
const blockProps = useBlockProps.save();
return (
<div {...blockProps}>
<RichText.Content tagName="p" value={content} />
</div>
);
}
Working with Block Attributes
Attributes define the data structure of your block:
Common Attribute Types:
string- Text contentnumber- Numeric valuesboolean- True/false valuesarray- Lists of itemsobject- Complex data structures
Example with Multiple Attributes:
attributes: {
title: {
type: 'string',
source: 'html',
selector: 'h2',
},
count: {
type: 'number',
default: 0,
},
isActive: {
type: 'boolean',
default: true,
},
items: {
type: 'array',
default: [],
},
},
Edit and Save Functions
Advanced Edit Component
import {
useBlockProps,
RichText,
InspectorControls
} from '@wordpress/block-editor';
import { PanelBody, RangeControl } from '@wordpress/components';
export default function Edit({ attributes, setAttributes }) {
const { title, count } = attributes;
const blockProps = useBlockProps();
return (
<>
<InspectorControls>
<PanelBody title="Settings">
<RangeControl
label="Count"
value={count}
onChange={(value) => setAttributes({ count: value })}
min={0}
max={100}
/>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<RichText
tagName="h2"
value={title}
onChange={(value) => setAttributes({ title: value })}
placeholder="Enter title..."
/>
<p>Count: {count}</p>
</div>
</>
);
}
Dynamic Save Function
For server-rendered content:
export default function save() {
return null;
}
Then register as dynamic block in PHP:
register_block_type('my-plugin/my-first-block', [
'render_callback' => 'render_my_block',
]);
function render_my_block($attributes) {
ob_start();
// Render your block HTML
return ob_get_clean();
}
Advanced Block Patterns
Nested Blocks
Allow child blocks within your block:
registerBlockType('my-plugin/container-block', {
// ... other config
supports: {
innerBlocks: true,
},
edit: ({ innerBlocks, setInnerBlocks }) => {
return (
<div {...useBlockProps()}>
<InnerBlocks />
</div>
);
},
save: () => {
return (
<div {...useBlockProps.save()}>
<InnerBlocks.Content />
</div>
);
},
});
Block Variations
Create variations of the same block:
registerBlockType('my-plugin/button-block', {
// ... base config
variations: [
{
name: 'primary',
title: 'Primary Button',
attributes: { variant: 'primary' },
},
{
name: 'secondary',
title: 'Secondary Button',
attributes: { variant: 'secondary' },
},
],
});
Best Practices
- Use semantic HTML in save function
- Implement proper accessibility (ARIA labels)
- Optimize block performance
- Follow WordPress coding standards
- Test blocks thoroughly before release
- Document block attributes and usage
- Provide default values for attributes
- Use WordPress i18n for translations
- Handle edge cases gracefully
- Keep edit and save functions in sync
Conclusion
Creating custom WordPress blocks opens up endless possibilities for content creation. Key takeaways:
- Blocks use React for the editor interface
- Attributes define block data structure
- Edit function controls editor experience
- Save function determines frontend output
- Follow WordPress best practices
Start with simple blocks and gradually add complexity. The WordPress block API is powerful and flexible, allowing you to create exactly what you need for your projects.
Written by Mahesh Waghmare
I bridge the gap between WordPress architecture and modern React frontends. Currently building tools for the AI era.
Follow on Twitter →