Extending Gutenberg Core Blocks with Custom Attributes and Controls

Extensibility is one of WordPress features that I love the most. From custom filters to actions, now they are applying the same thing with the new Gutenberg Editor.

It’s pretty great but aside from the WordPress handbook, there are only few tutorials available for Gutenberg development as of the moment. This is the main reason I’ve decided to pitch in and help out with creating tutorials starting from custom attributes and controls. These tutorials are the ones I’ve learned by actively participating on Gutenberg development on Github. I also used these methods on CoBlocks and EditorsKit plugins.

Let’s dig in. 😎

Adding Custom Attributes to Core Gutenberg Blocks

First, let’s add our custom attributes. In this tutorial, we will be using visibleOnMobile custom attributes to add custom CSS mobile-hidden class when it’s set to false.

By using blocks.registerBlockType filter we can extend each block setting, which includes the attributes.

Here’s how you can easily add our custom attribute. 👇

/**
* WordPress Dependencies
*/
const { addFilter } = wp.hooks;
/**
* Add custom attribute for mobile visibility.
*
* @param {Object} settings Settings for the block.
*
* @return {Object} settings Modified settings.
*/
function addAttributes( settings ) {
//check if object exists for old Gutenberg version compatibility
if( typeof settings.attributes !== 'undefined' ){
settings.attributes = Object.assign( settings.attributes, {
visibleOnMobile:{
type: 'boolean',
default: true,
}
});
}
return settings;
}
addFilter(
'blocks.registerBlockType',
'editorskit/custom-attributes',
addAttributes
);

Adding Custom Attributes to Core Gutenberg Blocks

Create Custom Controls on Advanced Block Panel

Now that we have registered our custom attributes, we need custom control to handle/change the value. I’ll be using ToggleControl component to easily switch our control on or off.

Mobile devices visibility toggle control

Using editor.BlockEdit filter, we can modify block’s edit component. It receives the original block via BlockEdit component and returns the new component. Then InspectorAdvancedControls component will help us target the Advanced Block Panel. It’s easier to show than tell. Check my codes below.

/**
* WordPress Dependencies
*/
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { InspectorAdvancedControls } = wp.editor;
const { createHigherOrderComponent } = wp.compose;
const { ToggleControl } = wp.components;
/**
* Add mobile visibility controls on Advanced Block Panel.
*
* @param {function} BlockEdit Block edit component.
*
* @return {function} BlockEdit Modified block edit component.
*/
const withAdvancedControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
const {
attributes,
setAttributes,
isSelected,
} = props;
const {
visibleOnMobile,
} = attributes;
return (
<Fragment>
<BlockEdit {…props} />
{ isSelected &&
<InspectorAdvancedControls>
<ToggleControl
label={ __( 'Mobile Devices Visibity' ) }
checked={ !! visibleOnMobile }
onChange={ () => setAttributes( { visibleOnMobile: ! visibleOnMobile } ) }
help={ !! visibleOnMobile ? __( 'Showing on mobile devices.' ) : __( 'Hidden on mobile devices.' ) }
/>
</InspectorAdvancedControls>
}
</Fragment>
);
};
}, 'withAdvancedControls');
addFilter(
'editor.BlockEdit',
'editorskit/custom-advanced-control',
withAdvancedControls
);

Apply Custom Element Class to Gutenberg Blocks

Almost done! All I need to do now is apply our mobile-hidden to the block. I just need to make sure that it will only be added when toggle is off.

blocks.getSaveContent.extraProps filter applies to all blocks returning the element in the save() function. This filter has extraProps attribute that we can use to add custom classNames.

/**
* External Dependencies
*/
import classnames from 'classnames';
/**
* Add custom element class in save element.
*
* @param {Object} extraProps Block element.
* @param {Object} blockType Blocks object.
* @param {Object} attributes Blocks attributes.
*
* @return {Object} extraProps Modified block element.
*/
function applyExtraClass( extraProps, blockType, attributes ) {
const { visibleOnMobile } = attributes;
//check if attribute exists for old Gutenberg version compatibility
//add class only when visibleOnMobile = false
if ( typeof visibleOnMobile !== 'undefined' && !visibleOnMobile ) {
extraProps.className = classnames( extraProps.className, 'mobile-hidden' );
}
return extraProps;
}
addFilter(
'blocks.getSaveContent.extraProps',
'editorskit/applyExtraClass',
applyExtraClass
);

That’s it! I made sure that it’s pretty easy to follow — I hope I achieved that. Now you can just add .mobile-hidden{ display: none } on your frontend css.

Below is the full code with dependencies and filters altogether. I’ve also added allowedBlocks variable so you can check how to apply the custom attributes and control to specific blocks only.

/**
* External Dependencies
*/
import classnames from 'classnames';
/**
* WordPress Dependencies
*/
const { __ } = wp.i18n;
const { addFilter } = wp.hooks;
const { Fragment } = wp.element;
const { InspectorAdvancedControls } = wp.editor;
const { createHigherOrderComponent } = wp.compose;
const { ToggleControl } = wp.components;
//restrict to specific block names
const allowedBlocks = [ 'core/paragraph', 'core/heading' ];
/**
* Add custom attribute for mobile visibility.
*
* @param {Object} settings Settings for the block.
*
* @return {Object} settings Modified settings.
*/
function addAttributes( settings ) {
//check if object exists for old Gutenberg version compatibility
//add allowedBlocks restriction
if( typeof settings.attributes !== 'undefined' && allowedBlocks.includes( settings.name ) ){
settings.attributes = Object.assign( settings.attributes, {
visibleOnMobile:{
type: 'boolean',
default: true,
}
});
}
return settings;
}
/**
* Add mobile visibility controls on Advanced Block Panel.
*
* @param {function} BlockEdit Block edit component.
*
* @return {function} BlockEdit Modified block edit component.
*/
const withAdvancedControls = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
const {
name,
attributes,
setAttributes,
isSelected,
} = props;
const {
visibleOnMobile,
} = attributes;
return (
<Fragment>
<BlockEdit {…props} />
//add allowedBlocks restriction
{ isSelected && allowedBlocks.includes( name ) &&
<InspectorAdvancedControls>
<ToggleControl
label={ __( 'Mobile Devices Visibity' ) }
checked={ !! visibleOnMobile }
onChange={ () => setAttributes( { visibleOnMobile: ! visibleOnMobile } ) }
help={ !! visibleOnMobile ? __( 'Showing on mobile devices.' ) : __( 'Hidden on mobile devices.' ) }
/>
</InspectorAdvancedControls>
}
</Fragment>
);
};
}, 'withAdvancedControls');
/**
* Add custom element class in save element.
*
* @param {Object} extraProps Block element.
* @param {Object} blockType Blocks object.
* @param {Object} attributes Blocks attributes.
*
* @return {Object} extraProps Modified block element.
*/
function applyExtraClass( extraProps, blockType, attributes ) {
const { visibleOnMobile } = attributes;
//check if attribute exists for old Gutenberg version compatibility
//add class only when visibleOnMobile = false
//add allowedBlocks restriction
if ( typeof visibleOnMobile !== 'undefined' && !visibleOnMobile && allowedBlocks.includes( blockType.name ) ) {
extraProps.className = classnames( extraProps.className, 'mobile-hidden' );
}
return extraProps;
}
//add filters
addFilter(
'blocks.registerBlockType',
'editorskit/custom-attributes',
addAttributes
);
addFilter(
'editor.BlockEdit',
'editorskit/custom-advanced-control',
withAdvancedControls
);
addFilter(
'blocks.getSaveContent.extraProps',
'editorskit/applyExtraClass',
applyExtraClass
);
view raw full-codes.js hosted with ❤ by GitHub

Closing

As you can see, this will open up a lot of possibilities to extend existing Gutenberg blocks. I highly suggest you explore the Gutenberg Handbook for more filters available. I urge you to subscribe to my newsletter too to get notified for new tutorials and news. With that being said, please share the love and do not hesitate to say hi on the comment section.

11 responses to “Extending Gutenberg Core Blocks with Custom Attributes and Controls”

  1. Hi

    Can you add information about file structure? There are some files, but where we should put all of them?

    Regards

    1. Jeffrey Carandang Avatar
      Jeffrey Carandang

      Thanks for dropping by! I’ll probably need to create a separate tutorial for that. For the mean time you can check the Gutenberg Handbook, I’ll let you know when the tutorial is available. Thanks!

      https://wordpress.org/gutenberg/handbook/designers-developers/developers/filters/block-filters/

  2. Would be great if this was also an example is ES5 (not ESNext) Not everyone is Reactive Native capable, and I was hoping to just copy/paste this into a JS file, but it generates and error.

    1. Jeffrey Carandang Avatar
      Jeffrey Carandang

      Thanks John! I’m planning to create one actually. I’ve got few requests on ES5 article. I’m just finishing up new features for EditorsKit and will get back to it 😉

  3. I guess this is a good tutorial for people who know what they are doing. But for people having no idea about the Gutenberg structure, this tutorial does not help.. Maybe as an intro add links to the necessary resources?

    I’ll probably come back to this tutorial at some point, but for now I don’t have a clue where I should put the code. It even took me a moment to realize it’s all JavaScript/React while I would have expected the code to be PHP. Maybe, at least mention this as well 🙂

    1. Jeffrey Carandang Avatar
      Jeffrey Carandang

      Thanks a lot for the feedback Jules! I’ve got a lot of similar response to this and my other tutorials so I’ve created this one : https://jeffreycarandang.com/create-gutenberg-block-plugin-wp-scripts-postcss-build/. This will help you get started 🙂 Let me know how it goes.

  4. Would the new attribute be available in an API call too? Trying out a headless install and wondering…

    1. Jeffrey Carandang Avatar

      Hi Josh! Are you using WP GraphQL? It’ll depends on the endpoints added via API. I’m not actually sure since I haven’t tried it out. Thanks!

  5. Hi Jeffrey, Looks great and thanks for the clear outline… so many examples make it over complicated. The only problem I have is the class is not applying in the editor or in the front end, any suggestion on how to debug? Cheers

  6. Hi Jeffrey, thank you for this tutorial! So far it has been very helpful, but I haven’t quite gotten it to work yet. I’m trying to run it just from a js file that I’m enqueuing in my functions.php; does it need to be in a plugin? It seems to not like the ESNext bit:
    Uncaught SyntaxError: Unexpected token ‘<'
    I saw some threads online where people were saying you need to run npm install && npm run build, but I don't have any kind of build script for it! Am I on the right track at all?

  7. Thanks for those tips, they’ve been really helpfull !

    One question remains though :
    if the ‘blocks.getSaveContent.extraProps’ filter allows us to apply our modifications on the final product (the block appearing on our website), how could one make those changes visible in the editor ?

    I guess i should modify the ‘editor.BlockEdit’ hook, but how can i access the block, and modify it accordingly with the newly set attributes ?

    I’m trying to style a simple heading block, adding so margin to it.
    The margin is applied on

Leave a Reply

I won’t send you spam. Unsubscribe at any time.

Discover more from Jeffrey Carandang

Subscribe now to keep reading and get access to the full archive.

Continue reading