Building Custom Gutenberg Blocks: A Complete Guide

Master the art of creating custom Gutenberg blocks for WordPress. Learn modern JavaScript, React patterns, and block development best practices.

WPBits Team
2 min read
#gutenberg #blocks #react #javascript #wordpress
Custom Gutenberg block development

Building custom Gutenberg blocks opens up endless possibilities for creating unique content experiences in WordPress. This guide will walk you through the entire process.

Setting Up Your Development Environment

Before diving into block development, you’ll need the right tools and setup.

Required Tools

  • Node.js (v18 or higher)
  • npm or yarn package manager
  • WordPress local development environment
  • Code editor with ESLint support

Installing @wordpress/create-block

The official scaffolding tool makes it easy to start:

Terminal window
npx @wordpress/create-block my-custom-block
cd my-custom-block
npm start

This creates a complete block development environment with hot reloading and modern JavaScript tooling.

Understanding Block Structure

Every Gutenberg block consists of several key files and concepts.

The block.json File

This is the heart of your block configuration:

{
"apiVersion": 3,
"name": "wpbits/custom-block",
"title": "Custom Block",
"category": "widgets",
"icon": "smiley",
"description": "A custom block example",
"supports": {
"html": false,
"align": true
},
"attributes": {
"content": {
"type": "string",
"source": "html",
"selector": "p"
}
}
}

The Edit Component

The edit component defines how your block appears in the editor:

import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function Edit({ attributes, setAttributes }) {
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<RichText
tagName="p"
value={attributes.content}
onChange={(content) => setAttributes({ content })}
placeholder="Enter your content..."
/>
</div>
);
}

The Save Component

The save component determines the 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>
);
}

Adding Block Controls

Enhance your block with inspector controls and toolbar options.

Inspector Controls

Add settings to the sidebar:

import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl, RangeControl } from '@wordpress/components';
function Edit({ attributes, setAttributes }) {
return (
<>
<InspectorControls>
<PanelBody title="Settings">
<ToggleControl
label="Enable feature"
checked={attributes.enableFeature}
onChange={(enableFeature) => setAttributes({ enableFeature })}
/>
<RangeControl
label="Columns"
value={attributes.columns}
onChange={(columns) => setAttributes({ columns })}
min={1}
max={4}
/>
</PanelBody>
</InspectorControls>
{/* Block content */}
</>
);
}

Block Toolbar

Add quick actions to the block toolbar:

import { BlockControls } from '@wordpress/block-editor';
import { ToolbarGroup, ToolbarButton } from '@wordpress/components';
function Edit() {
return (
<>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
icon="admin-links"
label="Add link"
onClick={handleAddLink}
/>
</ToolbarGroup>
</BlockControls>
{/* Block content */}
</>
);
}

Working with Attributes

Attributes store your block’s data and state.

Defining Attributes

In your block.json:

{
"attributes": {
"title": {
"type": "string",
"default": ""
},
"imageUrl": {
"type": "string"
},
"columns": {
"type": "number",
"default": 3
},
"showDescription": {
"type": "boolean",
"default": true
}
}
}

Using Attributes in Components

Access and update attributes in your components:

function Edit({ attributes, setAttributes }) {
const { title, columns, showDescription } = attributes;
const updateTitle = (newTitle) => {
setAttributes({ title: newTitle });
};
return (
<div>
<input
type="text"
value={title}
onChange={(e) => updateTitle(e.target.value)}
/>
</div>
);
}

Advanced Patterns

Take your blocks to the next level with these advanced techniques.

Dynamic Blocks

For blocks that need server-side rendering:

<?php
function wpbits_render_dynamic_block($attributes) {
$posts = get_posts([
'posts_per_page' => $attributes['postsToShow'],
'category' => $attributes['category']
]);
ob_start();
?>
<div class="wpbits-dynamic-block">
<?php foreach ($posts as $post): ?>
<article>
<h3><?php echo esc_html($post->post_title); ?></h3>
</article>
<?php endforeach; ?>
</div>
<?php
return ob_get_clean();
}
register_block_type('wpbits/dynamic-block', [
'render_callback' => 'wpbits_render_dynamic_block'
]);

Inner Blocks

Create container blocks that accept other blocks:

import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
function Edit() {
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<InnerBlocks
allowedBlocks={['core/paragraph', 'core/heading']}
template={[
['core/heading', { level: 2 }],
['core/paragraph', {}],
]}
/>
</div>
);
}

Testing and Debugging

Ensure your blocks work correctly across different scenarios.

Using wp-scripts

The official build tool includes testing utilities:

Terminal window
npm run test:unit
npm run lint:js
npm run format

Browser DevTools

Use React DevTools to inspect component state and props in the editor.

Best Practices

Follow these guidelines for maintainable, performant blocks:

  1. Use block.json: Define everything possible in block.json for better performance
  2. Minimize dependencies: Only import what you need from WordPress packages
  3. Validate attributes: Always validate and sanitize user input
  4. Support core features: Implement align, anchor, and other common supports
  5. Test thoroughly: Test in different themes and with various content
  6. Document your code: Add clear comments and documentation

Conclusion

Custom Gutenberg blocks are a powerful way to extend WordPress. With modern JavaScript, React patterns, and the WordPress block editor APIs, you can create sophisticated content editing experiences.

Start small, experiment often, and gradually build more complex blocks as you gain confidence. The WordPress block editor ecosystem is constantly evolving, so stay updated with the latest best practices and APIs.

Happy block building!