What You’ll Build
You’ll create a custom “Call to Action” block that allows users to:- Add a title and description
- Include a button with customizable text and link
- Style the block with custom colors
Prerequisites
Before starting, ensure you have:- Node.js v20.10.0+ and npm v10.2.3+
- A WordPress installation (local development environment recommended)
- Basic knowledge of JavaScript and React
- Familiarity with the terminal/command line
Step 1: Scaffold Your Block
Use the official@wordpress/create-block tool to generate your block’s foundation:
Replace
my-company with your own unique namespace. This helps prevent naming conflicts with other plugins.cta-block with the following structure:
Step 2: Navigate and Start Development
Start the Development Build
Step 3: Configure Block Metadata
Theblock.json file contains your block’s metadata and configuration. Update it with meaningful information:
src/block.json
Understanding Block Attributes
Attributes define the data your block stores. Each attribute has:- type: The data type (
string,number,boolean,array,object) - default: The initial value when the block is inserted
Step 4: Build the Edit Component
The edit component defines what users see when editing the block in the WordPress admin. Replace the contents ofsrc/edit.js:
src/edit.js
Key Concepts in the Edit Component
useBlockProps()
useBlockProps()
This React hook provides essential props for the block wrapper, including:
- Class names for styling
- Data attributes for WordPress
- Accessibility attributes
<div {...useBlockProps()}>RichText Component
RichText Component
RichText allows users to edit text inline with basic formatting. Key props:tagName: HTML element to render (h1, h2, p, span, etc.)value: The current text value from attributesonChange: Function to update the attributeplaceholder: Placeholder text when empty
InspectorControls
InspectorControls
This component adds controls to the block settings sidebar (right side of the editor). Use it for advanced options that don’t need to be inline.
Internationalization
Internationalization
The
__() function marks strings for translation. Always wrap user-facing text:Step 5: Build the Save Component
The save component defines the HTML saved to the database and rendered on the frontend. Updatesrc/save.js:
src/save.js
Step 6: Add Styles
Editor Styles
Add editor-specific styles insrc/editor.scss:
src/editor.scss
Frontend Styles
Add production styles insrc/style.scss:
src/style.scss
Step 7: Test Your Block
Add the Block to a Post
- Create a new post or page in WordPress
- Click the block inserter (+)
- Search for “Call to Action”
- Click to insert the block
Customize the Block
- Edit the title and description inline
- Click the block, then check the sidebar settings (right side)
- Enter a button URL in the “Button Settings” panel
- Use the block toolbar to change colors and spacing
Understanding the Block Lifecycle
Here’s how WordPress processes your block:Registration
src/index.js calls registerBlockType() with metadata from block.json, registering your block with WordPress.Insertion
When a user adds your block, WordPress:
- Creates a new block instance
- Initializes attributes with default values
- Renders the Edit component
Editing
As users interact with the block:
- Changes call
setAttributes()to update data - React re-renders the Edit component
- WordPress marks the post as having unsaved changes
Saving
When the user saves the post:
- WordPress calls your Save component with current attributes
- The returned HTML is serialized with block delimiters
- The result is saved to the database:
Dynamic Blocks (Advanced)
Static blocks save HTML to the database. Dynamic blocks render on the server using PHP, perfect for:- Displaying data that changes (latest posts, user info)
- Complex logic that shouldn’t run in JavaScript
- Better performance for heavy computations
--variant=dynamic flag:
render.php file instead of a save.js file. The PHP template renders the block on each page load.
Production Build
Before deploying your block, create an optimized production build:The production build removes development warnings, minifies code, and optimizes assets for better performance.
Next Steps
Now that you’ve built your first block, explore more advanced topics:Block Supports API
Add built-in features like alignment, colors, and typography
Block Patterns
Create reusable block layouts
InnerBlocks
Build blocks that contain other blocks
Block Editor Handbook
Complete documentation and API reference
Troubleshooting
Block doesn't appear in the inserter
Block doesn't appear in the inserter
- Ensure the plugin is activated
- Check that
npm startornpm run buildcompleted without errors - Look for JavaScript errors in the browser console
- Verify
block.jsonhas valid JSON syntax
'Block validation failed' error
'Block validation failed' error
This means the saved HTML doesn’t match the current Save component output. Common causes:
- You modified the Save component after saving content
- Class names or HTML structure changed
- Use browser DevTools to compare saved HTML with expected output
- Implement a block deprecation
- Clear and re-save affected posts
Styles not appearing
Styles not appearing
- Check that SCSS files are imported in
index.js - Verify webpack compiled successfully (check terminal)
- Hard refresh your browser (Ctrl+Shift+R)
- Check that class names in JS match those in SCSS
Attributes not saving
Attributes not saving
- Ensure you’re calling
setAttributes()in the Edit component - Verify attribute names match between
block.json, Edit, and Save - Check browser console for JavaScript errors
Additional Resources
- Block Editor Handbook - Official documentation
- Gutenberg GitHub Repository - Source code and issues
- Block Editor Components - Available UI components
- WordPress Developer Blog - Tutorials and updates