Blocks are the fundamental units of markup that compose content and layout in the WordPress block editor. This guide walks you through creating custom blocks from registration to implementation.
What is a Block?
A “block” is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage. The idea combines concepts of what in WordPress we traditionally achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience.
Block Registration
Every block starts by registering a new block type definition using the registerBlockType function from the @wordpress/blocks package.
Basic Block Structure
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
registerBlockType( 'my-plugin/my-block', {
title: 'My First Block',
description: 'A custom block for my plugin',
category: 'widgets',
icon: 'book-alt',
edit: () => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
Hello from the editor!
</div>
);
},
save: () => {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
Hello from the saved content!
</div>
);
},
} );
Block Name
The name for a block is a unique string that identifies it. Names must be structured as namespace/block-name, where namespace is the name of your plugin or theme.
// Good examples
registerBlockType( 'my-company-blocks/hero', {} );
registerBlockType( 'awesome-gallery-plugin/slideshow', {} );
// Avoid these - too generic
registerBlockType( 'create-block/example', {} );
registerBlockType( 'block/content', {} );
Important: Block names cannot be changed later without consequences. The block name is stored in the post content, so changing it requires editing all affected posts or running database scripts.
Namespace Best Practices
- Use your actual plugin/theme name:
my-awesome-plugin/block-name
- Avoid generic names like
editorial/, block/, or create-block/
- Use the same namespace for all blocks in your plugin/theme
- Make it unique to prevent conflicts with other plugins
Block Configuration Properties
Required Properties
title
The display title for your block, which can be translated. Appears in the inserter and throughout the editor.
category
Blocks are grouped into categories. Core categories include:
text
media
design
widgets
theme
embed
Optional Properties
description
A short description shown in the Block Tab in the Settings Sidebar.
description: __( 'Block showing a Book card.' )
icon
An icon to identify your block. Can be a Dashicon slug or custom SVG.
// Using a Dashicon
icon: 'book-alt'
// Using custom SVG
icon: <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M19 13H5v-2h14v2z" />
</svg>
// With colors
icon: {
background: '#7e70af',
foreground: '#fff',
src: <svg>...</svg>,
}
keywords
Terms that help users discover your block while searching.
keywords: [ __( 'image' ), __( 'photo' ), __( 'pics' ) ]
The Edit Function
The edit function defines what users see in the block editor when editing content.
Using useBlockProps
Always use useBlockProps() to ensure your block works correctly with the editor:
import { useBlockProps } from '@wordpress/block-editor';
edit: () => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
Editor content here
</div>
);
}
The useBlockProps hook:
- Adds necessary CSS classes
- Handles block supports attributes
- Ensures proper editor integration
Accessing Attributes
edit: ( { attributes, setAttributes } ) => {
const { content, alignment } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<input
value={ content }
onChange={ ( e ) => setAttributes( { content: e.target.value } ) }
/>
</div>
);
}
The Save Function
The save function defines the final markup saved to the database and rendered on the frontend.
import { useBlockProps } from '@wordpress/block-editor';
save: ( { attributes } ) => {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
<p>{ attributes.content }</p>
</div>
);
}
Always use useBlockProps.save() in the save function to ensure attributes from block supports are properly saved.
Installation
To use the blocks package:
npm install @wordpress/blocks --save
Creating Your First Block
Set up your development environment
Install Node.js and npm, then set up a build process for JSX.
Create your block registration file
Create a new JavaScript file and import the necessary functions:import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
Register your block
Call registerBlockType with your block name and configuration:registerBlockType( 'my-plugin/my-block', {
title: 'My Block',
category: 'widgets',
edit: () => {
return <div { ...useBlockProps() }>Editor view</div>;
},
save: () => {
return <div { ...useBlockProps.save() }>Saved view</div>;
},
} );
Enqueue your script
Register and enqueue your block script in PHP:function my_plugin_register_block() {
wp_register_script(
'my-plugin-block',
plugins_url( 'build/index.js', __FILE__ ),
array( 'wp-blocks', 'wp-element', 'wp-block-editor' )
);
register_block_type( 'my-plugin/my-block', array(
'editor_script' => 'my-plugin-block',
) );
}
add_action( 'init', 'my_plugin_register_block' );
Test your block
Open the WordPress editor and insert your new block from the inserter.
Block Collections
You can group related blocks into collections:
import { registerBlockCollection } from '@wordpress/blocks';
registerBlockCollection( 'my-plugin', {
title: 'My Plugin Blocks',
icon: <svg>...</svg>,
} );
All blocks with the my-plugin namespace will appear under this collection in the inserter.
Common Block Patterns
Simple Content Block
registerBlockType( 'my-plugin/simple-block', {
title: 'Simple Block',
category: 'text',
attributes: {
content: {
type: 'string',
default: '',
},
},
edit: ( { attributes, setAttributes } ) => {
return (
<div { ...useBlockProps() }>
<input
value={ attributes.content }
onChange={ ( e ) => setAttributes( { content: e.target.value } ) }
/>
</div>
);
},
save: ( { attributes } ) => {
return (
<div { ...useBlockProps.save() }>
{ attributes.content }
</div>
);
},
} );
Next Steps
API Reference
For complete API documentation, see: