So far we’ve built our own block. This tutorial shows how to extend other people’s blocks — particularly core blocks — using WordPress’s JS hook system. This is how block-toolbar plugins, format-bar additions, and custom inserter filters work.
The hook system
@wordpress/hooks is the JS equivalent of WP’s PHP hook system. Two flavors:
addFilter()— modify a value passing throughaddAction()— react to something happening
The hooks fire at well-defined points in the block lifecycle. Pick the right one for the modification you want.
Hook 1: blocks.registerBlockType — modify block settings
Fired when WordPress registers a block. Modify the settings object to add attributes, change icons, alter supports:
import { addFilter } from '@wordpress/hooks';
addFilter(
'blocks.registerBlockType',
'my-first-block/add-lock-attribute',
(settings, name) => {
if (name !== 'core/paragraph') return settings;
return {
...settings,
attributes: {
...settings.attributes,
isLocked: { type: 'boolean', default: false },
},
};
}
);
Now every paragraph has an isLocked attribute. Setting and using it is the next step.
Hook 2: editor.BlockEdit — wrap the edit component
Wrap a block’s Edit component with a higher-order component to add inspector controls, toolbar buttons, or wrappers:
import { addFilter } from '@wordpress/hooks';
import { createHigherOrderComponent } from '@wordpress/compose';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
const withLockToggle = createHigherOrderComponent((BlockEdit) => {
return (props) => {
if (props.name !== 'core/paragraph') return <BlockEdit {...props} />;
return (
<>
<BlockEdit {...props} />
<InspectorControls>
<PanelBody title="Custom" initialOpen={false}>
<ToggleControl
label="Locked"
checked={props.attributes.isLocked}
onChange={(value) => props.setAttributes({ isLocked: value })}
/>
</PanelBody>
</InspectorControls>
</>
);
};
}, 'withLockToggle');
addFilter('editor.BlockEdit', 'my-first-block/with-lock-toggle', withLockToggle);
Now every paragraph has a “Custom” inspector panel with a “Locked” toggle.
Hook 3: blocks.getSaveContent.extraProps — modify saved markup
Add HTML attributes to the saved output without rewriting Save:
addFilter(
'blocks.getSaveContent.extraProps',
'my-first-block/locked-class',
(props, blockType, attributes) => {
if (blockType.name !== 'core/paragraph') return props;
if (!attributes.isLocked) return props;
return {
...props,
className: `${props.className || ''} is-locked`,
'data-locked': 'true',
};
}
);
Now paragraphs with isLocked: true get class="is-locked" and data-locked="true" in the saved HTML.
SlotFill — adding to existing slots
Some Gutenberg UI areas (the document sidebar, the publish flow) are SlotFill points. Plugins can add their own panels:
import { registerPlugin } from '@wordpress/plugins';
import { PluginDocumentSettingPanel } from '@wordpress/editor';
import { TextControl } from '@wordpress/components';
const MyDocumentPanel = () => (
<PluginDocumentSettingPanel
name="my-plugin-panel"
title="My Plugin Settings"
icon="admin-plugins"
>
<TextControl label="External tracking ID" value="" onChange={() => {}} />
</PluginDocumentSettingPanel>
);
registerPlugin('my-first-block-document-panel', { render: MyDocumentPanel });
This adds a custom panel to the document sidebar in any post-edit screen.
When to extend vs fork
| Situation | Approach |
|---|---|
| Add a small option to a core block | Filter (this tutorial) |
| Need a totally different look | Build your own custom block |
| Change behavior for ALL blocks | Filter editor.BlockEdit with no type check |
| Replace a core block entirely | Use the block-deregister + replace pattern |
What’s next
Part 10 packages everything into a polished plugin — proper file structure, asset enqueuing, server-side render callbacks, plugin metadata.
Get the next one in your inbox. Practical tips, no fluff.
More tutorials
View all- WordPress
How to Prepare Your Plugin or Theme for WordPress 7.0
A step-by-step compatibility process for shipping a WordPress 7.0-ready release — set up a test site, audit your blocks, fix the breaking changes, and ship with confidence.
22 min
- WordPress
WordPress 7.0 for Block Developers: Breaking Changes
WordPress 7.0 enforces the iframed editor, broadens contentOnly mode, and drops PHP 7.3. Here are the breaking changes block and plugin developers must fix before users upgrade.
25 min
- WordPress
WordPress 7.0 AI: Client, Abilities & Connectors
The headline feature of WordPress 7.0 is native AI in Core. Here is what the WP AI Client, the Abilities API, and the Connectors system actually mean for plugin developers.
25 min