One thousand and one way to extend Gutenberg today

If there’s one and only one feature that made WordPress gain 30% of the web’s market share, this feature would be Extensibility. What is WordPress without Yoast? What is WordPress without ACF? Without JetPack? WooCommerce? Without Akismet? Without Contact Form 7? What is WordPress without Hello Dolly? It’s nothing but another niche CMS with way less than its current market share.

So yes, Extensibility is key, and we can’t build the next generation of the WordPress Editor without making it extensible. What does “making it extensible” mean? Does this mean providing hooks and filters in JavaScript the same way we do in PHP? Does this mean allowing plugins to extend and tweak any UI component used by Gutenberg? Does this mean adding filters to the REST API? Does this mean Giving access to an Editor object to manipulate the editor’s content?

There’s no clear answer to all these questions. Extensibility is a very difficult problem and unless you build it with real use-cases in mind, you’re more likely to get it wrong. (and being stuck with it for a long time).

So let’s take a look a closer look at Extensibility in Gutenberg and what we can achieve as a third party plugin right now.

Creating blocks

As you may have heard, Gutenberg is about blocks (or content blocks) you combine to compose content. A block that can represent any visual element: A heading, a paragraph, an image, an embed, a table, a list, a form, a dynamic list of posts, …

You can think of blocks as shortcodes v2 with a better UI to add and modify them. Blocks can be static (just rendering some static HTML) or dynamic (changing over time and rendered server side).

If the editor is made up of blocks, the most natural way to extend it is by creating blocks.

1- Create a static block

So our first example will be to add a new static block we’ll call it “Alert” displaying an alert message, The alert can have three states (warning, error or success) and has a message.

This block could look like this (minus the control to switch the type, just to make it simpler):

// myblock.js

var el = wp.element.createElement,
	registerBlockType = wp.blocks.registerBlockType;

registerBlockType( 'riad/alert', {
	title: 'Alert Block',
	icon: 'warning',
	category: 'common',

	attributes: {
		type: {
			type: 'string',
			default: 'danger',
		},
		message: {
			type: 'string',
			source: 'html',
			selector: 'div',
		},
	},

	edit: function( props ) {
		var className = props.className;
		var type = props.attributes.type;
		var message = props.attributes.message;
		function updateMessage( event ) {
			props.setAttributes( { message: event.target.value } );
		}

		return el(
			'p', 
			{ className: className + ' alert-' + type },
			el(
				'textarea',
				{ value: message, onChange: updateMessage }
			) 
		);
	},

	save: function( props ) {
		var type = props.attributes.type;
		var message = props.attributes.message;


		return el(
			'p', 
			{ className: 'alert-' + type },
			message
		);
	},
} );

Provide some styles like this

/* myblock.css */

.wp-block-riad-alert.alert-danger { color: red; }
.wp-block-riad-alert.alert-success { color: green; }
.wp-block-riad-alert.alert-warning { color: orange; }

And load this block in my plugin

<?php
// plugin.php

function riad_enqueue_blocks() {
	wp_enqueue_script(
		'riad-block',
		plugins_url( 'block.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element' )
	);
}
add_action( 'enqueue_block_editor_assets', 'riad_enqueue_blocks' );

function riad_enqueue_styles() {
	wp_enqueue_style(
		'riad-block-style',
		plugins_url( 'myblock.css', __FILE__ ),
		array( ),
		filemtime( plugin_dir_path( __FILE__ ) . 'myblock.css' )
	);
}
add_action( 'enqueue_block_assets', 'riad_enqueue_styles' );

So what’s happening here:

  • A javascript file to register the block providing some general information (an icon, a category, and a title), an edit function representing the visual state of the block in the editor and a save function representing the HTML output by this block,
  • A stylesheet adding some default styles to this block, extendable by themes,
  • And just a bunch of classic plugin hooks to tie everything together.

I won’t go too deep into the details here, if this is the way you want to extend Gutenberg, I encourage you to take a look at the Gutenberg Docs as this is heavily addressed.

2- Create a dynamic block

The second kind of blocks you can add to Gutenberg is the dynamic blocks. Let’s say we want to highlight the last published post in several other pages/posts. To do so we’ll create a dynamic block showing the title and a link to the latest post.

// myblock.js

var el = wp.element.createElement,
    registerBlockType = wp.blocks.registerBlockType,
    withSelect = wp.data.withSelect;

registerBlockType( 'riad/latest-post', {
    title: 'Latest Post',
    icon: 'megaphone',
    category: 'widgets',

    edit: withSelect( function( select ) {
        return {
            posts: select( 'core' ).getEntityRecords( 'postType', 'post' )
        };
    } )( function( props ) {
        if ( props.posts && props.posts.length === 0 ) {
            return "No posts";
        }
        var className = props.className;
        var post = props.posts[ 0 ];

        return el(
            'a',
            { className: className, href: post.link },
            post.title.rendered
        );
    } ),

    save: function() {
        // Rendering in PHP
        return null;
    },
} );

And add the server-side rendering like so

<?php
// block.php

function riad_render_block_latest_post( $attribites ) {
	$recent_posts = wp_get_recent_posts( array(
		'numberposts' => 1,
		'post_status' => 'publish',
	) );
	if ( count( $recent_posts ) === 0 ) {
		return 'No posts';
	}
	$post = $recent_posts[ 0 ];
	$post_id = $post['ID'];
	return sprintf(
		'<a class="wp-block-riad-latest-post" href="%1$s">%2$s</a>',
		esc_url( get_permalink( $post_id ) ),
		esc_html( get_the_title( $post_id ) )
	);
}

register_block_type( 'riad/latest-post', array(
	'render_callback' => 'riad_render_block_latest_post',
) );

And that’s it! Notice:

  • The edit function still shows a representation of the block in the editor’s context (this could be very different from the rendered version, it’s up to the block’s author)
  • The save function just returns null because the rendering is performed server-side.
  • The server-side rendering is a function taking the block attributes as an argument and returning the markup (quite similar to shortcodes)

3- Saving to Post Meta

“Ok! now I get it, creating blocks is cool, it replaces static and dynamic shortcodes, but what about metaboxes

Metaboxes are used for everything in the current WordPress’s editor. One of these use-cases (the most frequent one) is adding a custom post meta.

Blocks address this use case quite nicely by allowing plugin authors to declare meta attributes, block attributes retrieved and saved to a post meta:

// myblock.js

var el = wp.element.createElement,
	registerBlockType = wp.blocks.registerBlockType;

registerBlockType( 'riad/book-title', {
	title: 'Book Title',
	icon: 'book',
	category: 'common',

	attributes: {
		title: {
			type: 'string',
			meta: 'book-title',
			source: 'meta'
		},
	}

	edit: function( props ) {
		var className = props.className;
		var title = props.attributes.title;
		function updateTitle( event ) {
			props.setAttributes( { title: event.target.value } );
		}

		return el(
			'p', 
			{ className: className },
			el(
				'textarea',
				{ value: title, onChange: updateTitle }
			) 
		);
	},

	save: function() {
		return null;
	},
} );

Due to some REST API limitations, you also have to register the custom meta to your post type.

function gutenberg_my_block_init() {
	register_meta( 'post', 'book-title', array(
		'show_in_rest' => true,
		'single' => true,
	) );
}
add_action( 'init', 'gutenberg_my_block_init' );

Done! we have a block saving a book title to a custom post meta.

Removing Blocks

Adding blocks is easy enough, but one of the concerns we hear a lot from agencies about is that Gutenberg gives a lot of power to their clients and they won’t be able to control what markup their users will produce using Gutenberg.

I do think this concern is irrelevant. Gutenberg offers exactly that: a way to have a controlled set of blocks. We can’t mess up with the post content as easily as in the current editor.

1- Black List

Either way, if you think a block is irrelevant for you or for a specific block type, just unregister it. For example, if I don’t like the verse block because I’m not a poet, I can just remove it like so:

// myplugin.js

wp.blocks.unregisterBlockType( 'core/verse' );

Load my script in Gutenberg

<?php
// myplugin.php

function riad_blacklist_blocks() {
	wp_enqueue_script(
		'riad-blacklist',
		plugins_url( 'myplugin.js', __FILE__ ),
		array( 'wp-blocks' )
	);
}
add_action( 'enqueue_block_editor_assets', 'riad_blacklist_blocks' );

Easy enough! right!

2- White list

Some people do want finer control over the allowed blocks, let’s update our code above to use a whitelist instead:

// myplugin.js
var allowedBlocks = [
  'core/paragraph',
  'core/image',
  'core/html',
  'core/freeform'
];

wp.blocks.getBlockTypes().forEach( function( blockType ) {
  if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
    wp.blocks.unregisterBlockType( blockType.name );
  }
} );

Modifying blocks

Since we can register and unregister a block, this also means, to modify an existing block we can unregister it, modify it and register it back.

1- Changing a block’s title

Say I don’t like the “paragraph” block’s title, I want it to be “text”, I can do it:

// extend-block.js

var paragraphBlock = wp.blocks.unregisterBlockType( 'core/paragraph' );
paragraphBlock.title = "Text";
wp.blocks.registerBlockType( 'core/paragraph', paragraphBlock );

2- Adding a caption

Now that you got the principal, let’s do bigger modifications. What if we want to add a “caption” to the “code” block. To achieve this, we can “decorate” the edit , save and attributes like so:

// extend-block.js

var el = wp.element.createElement;
var RichText = wp.editor.RichText;
var codeBlock = wp.blocks.unregisterBlockType( 'core/code' );
codeBlock.attributes.caption = { source: 'children', selector: '.my-caption' };
var OriginalBlockEdit = codeBlock.edit;
var OriginalBlockSave = codeBlock.save;
codeBlock.edit = function( props ) {
  return [
    el( OriginalBlockEdit, Object.assign( props, { key: 'original' } ) ),

    el( RichText, {
        key: 'caption',
        tagName: 'div',
        className: 'my-caption',
        value: props.attributes.caption,
        onChange: function( value ) {
            props.setAttributes( { caption: value } );
        },
    } ),

  ];
};
codeBlock.save = function( props ) {
  return [
    el( OriginalBlockSave, Object.assign( props, { key: 'original' } ) ),

    el( RichText.Content, {
        tagName: 'div', 
        key: 'caption',
        className: 'my-caption',
        value: props.attributes.caption,
    } ),

  ];
};
wp.blocks.registerBlockType( 'core/code', codeBlock );

With this technique, you can imagine enhancing any existing Block easily.

3- Transforming blocks

Gutenberg has a built-in transformations API allowing third-party plugins to create blocks and define how these blocks could be transformed to (or from) any other block including core blocks.

As an example, let’s add a transformation to the “alert” block we created above to allow turning it into a regular paragraph block

// myblock.js

registerBlockType( 'riad/alert', {
  // ...
  transforms: {
    to: {
      type: 'block',
      blocks: [ 'core/paragraph' ],
      transform: function( attrs ) => {
        return wp.blocks.createBlock( 'riad/paragraph', {
          content: attrs.message,
        } );
      },
    },
  },
} );

The transformations API is not documented yet because it’s still subject to change but it’s a powerful API, it allows way more than this. Some of the use-cases covered by this API are:

  • Paste transformations: Transforming pasted content into a specific block depending on the pasted content
  • Patterns transformations: Allow shortcuts: For example, typing `# ` in an empty paragraph creates a heading block, this behavior can be extended to any other prefix and block.
  • Drag and Drop transformations: Drag and dropping a PDF file into the editor creates a custom PDF block

Styling

1- Styling blocks

One of the most frequent questions we’re being asked is how Gutenberg affects existing themes and how can theme authors enhance the Gutenberg Experience.

Blocks come with unopinionated styles built-in. This makes basically any WordPress theme out there compatible with Gutenberg without any change.

However, to give them more personality themes could easily tweak the styling of any blocks and Gutenberg makes this easy by having a well-defined output markup for each block and a generated class name for most blocks.

Let’s say I want to give the pullquote block a specific design in my theme, It’s just a matter of styling the `wp-block-pullquote` className

/* theme.css */

.wp-block-pullquote {
  background: #EEE;
  border: none;
}

2- Styling the editor

But what If I want to mimic a frontend styles and make the editor show blocks exactly how my theme would do it? To achieve this, just isolate the content’s specific styles in a stylesheet blocks.css loaded by both the frontend’s pages and the editor and load this stylesheet in the Gutenberg’s editor like so:

// functions.php

function my_theme_editor_styles() {
    wp_enqueue_style( 'theme-blocks-style', get_template_directory_uri() . '/blocks.css');
}
add_action( 'enqueue_block_editor_assets', 'my_theme_editor_styles' );

Theme Support

Themes can also extend Gutenberg using the “theme support” API. For now, two options are available: provide a color palette and enable wide alignments.

Several block types make use of “colors” and to ensure a seemless experience across blocks, define a color palette like so:

// functions.php

function mytheme_setup_theme_supported_features() {
    add_theme_support( 'editor-color-palette', array(
        array(
            'name' => __( 'strong magenta', 'themeLangDomain' ),
            'slug' => 'strong-magenta',
            'color' => '#a156b4',
        ),
        array(
            'name' => __( 'light grayish magenta', 'themeLangDomain' ),
            'slug' => 'light-grayish-magenta',
            'color' => '#d0a5db',
        ),
        array(
            'name' => __( 'very light gray', 'themeLangDomain' ),
            'slug' => 'very-light-gray',
            'color' => '#eee',
        ),
        array(
            'name' => __( 'very dark gray', 'themeLangDomain' ),
            'slug' => 'very-dark-gray',
            'color' => '#444',
        ),
    ) );
}

add_action( 'after_setup_theme', 'mytheme_setup_theme_supported_features' );

What’s next?

When extending Gutenberg, the first question you need to ask yourself is: Could this be done within a block? In most cases, the answer to this question is YES. But this doesn’t mean Gutenberg won’t allow developers to extend other UI elements.

Let’s take a look at the different extension APIs being considered in Gutenberg.

1- Extending blocks

As I showed you earlier, it’s possible to modify existing blocks by unregistering and registering them with a modified version. In the future, registering core blocks by third-party plugins may be prohibited and a simpler API to extend blocks will be provided.

2- Slots

Internally, Gutenberg makes an extensive use of “Slots“. Slots are pluggable UI areas. In the future, Gutenberg will more likely expose those slots to third-party plugins to allow extending areas like the Sidebar and the header allowing use-cases like:

  • Adding a panel to the post settings sidebar.
  • Adding a button to the editor’s header.
  • Adding an item to the editor’s mode switcher dropdown.

3- Decorators

While slots are great to enable adding items to the UI, they don’t allow to modify existing UI components. Gutenberg is built using reusable components and one way to extend the behavior of several UI elements of Gutenberg could be to modify these usable elements. Decorators are a generic pattern explored in Gutenberg allowing extensions to any component. Decorators can be used to allow the following use-cases:

  • Extend the `Editable` component to add “footnotes” to all text-based blocks
  • Extend the publish button and change it’s background if the post content is not HTML valid (or any other check)
  • Remove some components by extending them with “empty” components.

4- Other higher-level extensions

In addition to handling blocks (adding, removing, extending), Gutenberg will more likely allow more higher-level extensions API like registering new block categories, extending the text patterns

5- Block Templates

This is one of my favorite considered extension API, block templates. Block templates are a list of pre-filled blocks with placeholders, some of these blocks are “locked”, they can’t be moved or removed. Imagine a “product” custom post type defining a template with an image block, a text block for the description, a price block. And when a user decides to add a new product, the editor is already prefilled with these blocks. All what‘s left is filling the placeholders. Isn’t this a great experience for Custom Post Types?

Conclusion

Building extensibility APIs is hard, and replicating the actions and filters we’re using in PHP plugins may not be the best option for longer-term viability.  Relying on real use-cases and building generic patterns around them is what would lead to the best possible extensibility API for Gutenberg and the future of JavaScript-based WordPress plugins.

If you’re a plugin/theme developer, I encourage you to try the tips exposed in this post and share your extensibility use-cases with the Gutenberg team by replying to existing issues or creating new ones.

Thanks Grzegorz Ziółkowski for the feedback


This post is brought to you by the new Gutenberg Editor 


93 responses to “One thousand and one way to extend Gutenberg today”

  1. Riad,

    With Gutenberg changing so rapidly, before I dig in some more, are all of these examples still relevant? Or has things changed enough where some of these examples may not work the same as they did when you wrote this post?

  2. Are there any recommendations on how to support static blocks long-term as they evolve?

    For example, if we had a block that saved `My content` and included css for `.my-block{}` but later we decided to call it `.my-custom-block`, typically a developer would likely update the JS that saves the block, and update the CSS file changing `.my-block` to `.my-custom-block`, but that would effectively break the styles for old content saved as `.my-block`. . .so a developer would need to maintain `.my-block` and `.my-custom-block` styles to ensure old content still looks like it should, and new content looks like it should as well.

    Are there any good patterns/mechanisms in place to help support these scenarios, as I imagine blocks will evolve fairly quickly, and supporting the evolution is likely not something most folks will be considering.

    Just curious if ya’ll have recommendations/suggestions for how to support these scenarios long term.

  3. So for static blocks, several things here:

    – You can remove old blocks if you create new ones, these deleted blocks will fallback to the classic text block if no corresponding block is found

    – You should keep the old styles even if you delete the block

    – If you keep the same name and modify the output of the block, you can declare a “deprecated” attribute containing the “save” and “attributes” declarations of the old block, this ensure the block is updated properly when you open it in the editor. ( Details about this here https://github.com/WordPress/gutenberg/pull/3665 ) this is not documented yet.

    I hope this helps.

  4. Hi Riad,

    Interesting reading thank you.

    Does Gutenberg support dynamic block types in which a user can populate a collection of items.
    A typical example would be a post list builder in which you can create / remove / update / delete post cards.
    The post list block would also have a set of meta properties like the number of columns or the style of the post cards.

    If not yet, is this type of CRUD block scheduled to be supported in the future ?
    Merci beaucoup !
    Nicolas

    • Glad you liked the post!

      So yes, Gutenberg support dynamic blocks. I think we have two built-in dynamic blocks: Recent Posts and Categories. (See the “2- Create a dynamic block” section in this post for the API)

      Bon courage 😉

      • Yes I’ve read the section about Dynamic block and tested the Recent Posts with Gutenberg 1.9.1. That’s why I decided to ask you the question 😀

        The Recent Posts block fetches content dynamically. We can set the number of posts and other global options. So, the collection of posts is built automatically with the options of the block, but we don’t have a granular control on each item of this collection.

        What I would like to have is a way to actually build a collection of posts, post by post.

        A better example to illustrate this idea might be a Slider Block, allowing to create / edit / reorder / remove slides, each slides having the same model with customizable properties, for example { image attachment id, title, button text }.
        In the “Block” settings panel, this would require a way to dynamically add /edit / reorder / remove items . I’m not sure if the current implementation of Gutenberg can offer this type of multi-item type of blocks, can it ? Is this scheduled ?
        Thank you, I hope this makes more sense 🙂

      • I see no blocking in building a block like this. Gutenberg has no limitation in term of block content. If the content is dynamic it should be rendered server-side, if it’s static, it’s render client-side.

        Your block is probably dynamic because it contains posts (which may change over time). Whether the block is dynamic or static don’t have any influence on the UI you build to edit this bloc.

  5. This is a very powerful replacement for metaboxes, but it’s quite cumbersome as a replacement for basic custom fields. Are there plans to support arbitrary key-value custom fields in Gutenberg without requiring each potential key to have a block type defined?

  6. Hi riad
    I want to extend radio control of the inspector-controls to create radio-image controls.
    I ‘ve made some google search on decorators and slots but find nothing.
    Do you know where I can find some info about that ?

  7. Riad, thanks, this post is still the most useful/accessible one I’ve found on augmenting blocks.

    With regard to the Gallery Block, I suspect a few front end developers with custom sliders (or maybe it’s just me) would like to know how to change the gallery block? (I’ve been looking everywhere and haven’t found anything helpful yet. I find the source code for this aspect a little unapproachable, so in brief some very useful aims would be to:

    1. register a new instance of the gallery with a new name (summarised above thanks)
    2. programmatiacally assign an additional class-name for use by front end jquery code [I have several different types of gallery that can be implemented in any combination by the user]
    3. programmatically set image crop
    4. programmatically hide toolbars and inspector (where this is determined by the front end)

    I’m using Object.assign() with deregistered blocks to allow me to duplicate blocks with unique attributes, but I’m unable to figure out which attributes to edit to implement the changes needed.

    Could you indicate where I can look to find out about any of the above? it would be greatly appreciated (I’m sure there are other developers with custom front end slider implementations??).

    • 1. For complex blocks like the Gallery, I’d recommend building a separate slider/gallery blocks with maybe the possibility to transform back and forth to a regular gallery block. There’s an undocumented “transform” API. We’ll document it once the API is stable.

      > 2. programmatiacally assign an additional class-name for use by front end jquery code

      In a controlled world (React, vdom etc…) I don’t recommend touching dom nodes directly. You better use an attribute or a state variable that changes the className.

      > 3. programmatically set image crop

      Same here, just use an attribute to store the crop flag (or data) and apply this in your edit/save functions

      > 4. programmatically hide toolbars and inspector

      Can I ask why you’d want to do this in a plugin? In Gutenberg we’re trying to bring consistency to the Edit Screen. So Extensibility APIs needs to be consistent. we do not want to allow any change to the screen but offer the “right” APIs to achieve most use-cases.

      • Many thanks for the speedy response Riad, I guess I just need to get my head around React – to begin with. With regard to your last question the html generated by the gallery plugin is completely reformatted by my slider plugins – which look for a a group of images and image info under a class wrapper – so things like column numbers crop and alignment settings ore completely overridden. As such I wouldn’t want to mislead my users. I’m trusting that my scenario isn’t too unique, and hoping that it’s useful for other developers also?

        I’ll wait the transform API documentation thanks. Ideally I’d be able to write my own block from scratch but handling the image media library API in an editable way as the gallery block has done already is beyond my ability or any documentation I’ve found.

      • > things like column numbers crop and alignment settings ore completely overridden.

        If you build your block as a separate block, do not include those controls and nothing will show up on the sidebar. The block inspector (sidebar) is controlled by the block itself. You include what you want to include there.

        > handling the image media library API in an editable way as the gallery block has done already is beyond my ability or any documentation I’ve found.

        If you take a look at the built-in Gallery block https://github.com/WordPress/gutenberg/blob/master/blocks/library/gallery/block.js#L149-L161 you’ll notice that we use `MediaUploadButton` component which is a reusable component you can use on your own block too trigger the media library.

  8. Hi riad
    Thanks for this article I just wonder if the simpler API to extend blocks you’re talking about is finished now because I see the issue link (Possibility to extend any block settings from the plugin?) has been closed

  9. Hi Riad, Amazing article. There is error in save method of first example. You have missed the ‘type’ variable name. Should be like this

    save: function( props ) {
    var type = props.attributes.type;
    …….

  10. Hi, Riad.
    Thanks for this helpful article.
    Could you please do some example about using filter hook?
    I have read the handbooks but when I do as following it gives me nothing 🙁
    It is very awesome if you can write an example in details.
    Thanks a lot.

  11. Hi Riad
    With your article you give me the wish to build my first gutenberg block 😀
    I have built a tabs component but I have one question. Is there a repeater in gutenberg ?
    I mean for example for tabs component it would be great if a user can add as new tabpanel to his tabs as he wanted.
    Because right now my tabs component are quite static there’s three text fields for titles and three other for content.
    Thanks

  12. Hey Riad great article and in general great job done for gutenberg
    I notice that we can delete image when we use gallery block but we can’t delete column in the text-columns block
    Is it possible to add

    to each column in order to delete each one like in the image gallery ?

    • Hey Thanks!

      You can delete columns by changing the number of columns from the block settings menu (sidebar), but you can still open an issue in Gutenberg to add a button like the gallery block.

  13. Nice tutorial about Gutenberg! Thanks!

    Just a question about inspector controls. Can they (textarea, select, range, etc…) be used in blocks? Or just in the sidebar? So, if, for example, I need to add a datepicker in a block, do I have to create it myself or can I use an existing inspector control?

    Thanks in advance

    • Sure, the inspector controls are just reusable React components you could use in any place you like. You may have to tweak the styling though.

      Enjoy hacking

  14. Thanks for this tuto riad
    I just have one question about RadioControl
    I try to use it like this
    this.setState({ selected: e.target.instanceId})}
    options= radioOptions
    />
    but I don’t know what to do with help and instanceId.
    Are instanceId generated with a gutenberg function or do I have to create one ?
    Thanks

  15. Great post Riad
    I’ve seen nested block have been merged recently.
    It’ll probably offer a huge possibility for extending gutenberg.
    Is it possible to get the list of component inside the nested block ?
    For adding onscroll classes to some of them for example

  16. Salut Riad,

    Merci pour ce post instructif. Je suis en train de tester Gutenberg que j’aime beaucoup.
    J’ai besoin de rajouter une classe unique pour chaque bloc. J’ai réussi à le faire en utilisant les filtres blocks.registerBlockType et blocks.getSaveContent.extraProps. Mais lorsqu’on copie-colle 1 bloc, le problème est que les 2 blocs ont la même classe. Vois-tu un moyen d’y remédier?

    • Pour le moment, je ne vois pas vraiment de solution mais à terme il y’aura une solution en utilisant le data module https://github.com/WordPress/gutenberg/tree/master/data

      Il est possible d’être notifié lorsque le nombre de blocs changent (c’est pas documenté encore) mais ce qui n’est pas encore possible c’est de modifier des blocs en utilisant le data module. (genre modifier un attribut d’un bloc par code)

  17. I’ve been able to successfully create new blocks, however I’m attempting to modify the core/freeform block (to add a select field to the Inspector panel) and I’m getting a JS error that the block is undefined.

    Am I enqueuing the script too early? I’ve set wp-blocks as a dependency, just not sure where the break is.

  18. J’ai parcouru le code et la doc mais je n’ai pas trouvé s’il existe une fonction permettant d’insérer du contenu (et non un bloc) au niveau du curseur:
    J’ai rajouté un bouton dans l’inspecteur et au click je voudrais par exemple que cela insère le contenu suivant:

    Heading

    Y a t-il une function du genre insertContent( content ) ?
    Merci

    • Les commentaires HTML ont été filtrés dans mon message précédent. Autour de Heading il y a les commentaires HTML générés par Gutenberg:
      — wp:heading —
      Heading
      — /wp:heading —

    • Pas actuellement, mais ça sera possible dans la prochaine version je pense. Quelque chose comme `wp.data.dispatch( ‘core/editor’ ).insertBlocks( blockToInsert )`

  19. This is great! Riad, can you post a tutorial of how to add contextual tinymce buttons when editing text ?

    • Thanks, unfortunately I won’t have time to add such a tutorial, it should be similar to the extending block example. Some work might be done later in Gutenberg to make this easier.

  20. Salut Riad
    Merci pour ce petit tour d’horizon des possibilités qu’offre gutenberg.
    J’ai essayé d’ajouté qqch qu’on voit souvent sur les sites, les scroll animation et j’avoue que ca bloque sans que je comprenne pourquoi, j’ai un code simple:
    const { __ } = wp.i18n;
    const { registerBlockType ,createBlock, InnerBlocks } = wp.blocks;
    const { Component } = wp.element;
    import “animate.css/animate.min.css”;
    import ScrollAnimation from ‘react-animate-on-scroll’;

    class ScrollAni extends Component {
    render() {
    return (

    );
    }
    }

    const blockAttributes = {
    align: {
    type: ‘string’,
    default: ‘none’,
    }
    };

    registerBlockType( ‘gb/anime-jsx-example’, {
    title: __( ‘animeassis’ ),
    description: __( ‘animation’ ),
    icon: ‘laptop’,
    category: ‘common’,
    keywords: [ __( ‘animation’ ), __( ‘anime’ ) ],
    attributes: blockAttributes,

    edit() {
    return (

    );
    },

    save : ScrollAni,

    } );
    Est ce que tu sais ce qui bloque ici ?

    • Je n’ai pas compris le soucis, là tu déclares un bloc vide et tu importe ScrollAnimation que tu n’utilise nul part, le problème vient surement de là.

      Il faut aussi savoir que React est seulement chargé dans l’éditeur dans tous les composants React que tu utiliseras tel que ScrollAnimation ne marcheront que dans le backend ainsi.

    • Non pour utilisez React dans le frontend, il faut faire un script séparé chargé avec les enqueue script qui vont bien. et loader son application React soit même: `wp.element.render`.

      Malheureusement, je n’ai pas d’exemple sous la main qui fait ça, ce n’est pas très commun comme besoin. Tu pourrais aussi inclure ton scroll animation dans ton theme, parce que ce n’est pas réellement un bloc je pense.

  21. Riad I have two questions about dynamic block: when I have a dynamic block am I forced to use a php file for the frontend render or can I use the save function like for other block ? and the second is how do you do for enqueuing a javascript file in the block.php.
    Thanks for this instructive article

    • Yes, you have to use PHP to render dynamic blocks because the blocks are not even loaded in the frontend it’s just HTML so you need to generate this HTML server-side. You can use the `save` function in dynamic blocks as a fallback HTML (for example when you disable your plugin, this HTML will be shown).

      Not sure about the second question, if you ask how do you enqueue JavaScript in the frontend, there’s no change in Gutenberg about that, you’d do it like today. In your theme or using the regular enqueue scripts functions.

    • I wonder if the register block type take in account the enqueue script if I do it like this:
      function wpdocs_theme_name_scripts() {
      wp_enqueue_script( ‘script-name’, get_template_directory_uri() . ‘/js/example.js’, array(), ‘1.0.0’, true );
      }
      add_action( ‘wp_enqueue_scripts’, ‘wpdocs_theme_name_scripts’ );

      function riad_render_block_latest_post( $attribites ) {

      }

      register_block_type( ‘riad/latest-post’, array(
      ‘render_callback’ => ‘riad_render_block_latest_post’,
      ) );

      and also we had attributes for sharing data between edit and save but how can we share data between edit and php ?

      • This probably works, I guess it will enqueue the scripts in the frontend as well (make sure it doesn’t break because of missing dependencies).

        And attributes (comment attributes) are shared with the php side as well (first argument of the render_callback). Can I ask what’s this block for?

  22. Hi Riad,
    I’m trying to extend the MoreMenu by adding a link under the Tools group which should open a modal window.
    I’ve managed to do it (https://git.io/vx25t) but I encounter one issue: once the modal is opened and I click anywhere on it, it disappears.
    I see that it comes from the fact that the MoreMenu Dropdown disappears when a “clickOutside” event occurs but I don’t see a way to disable it.
    Do you see any workaround?

    Thanks

    • Not certain how to fix that for the moment but what I know is that an official way to add plugins opening modals from the MoreMenu is in the works for the upcoming releases.

  23. Riad I’ve notice there’s multiple source for the same function
    for example If you type hasBlockSupport in the search you find: import { getBlockType, hasBlockSupport } from ‘@wordpress/blocks’; and import { hasBlockSupport } from ‘../api’; These function are different and if so how could I import in my plugin these one : import { hasBlockSupport } from ‘../api’; ?

    • These are the same functions, inside the blocks module we use relative imports but we use the globals outside. In your block you can do `wp.blocks.hasBlockSupport`

  24. I’ve build my first block, a slider block but I face an unexpected problem:
    Block validation: Expected token of type `EndTag` (
    Object { type: “EndTag”, tagName: “div” }
    ), instead saw `StartTag` (
    Object { type: “StartTag”, tagName: “div”, attributes: […], selfClosing: false }
    )
    The problem come from my edit function I have

    { images.map( ( img, index ) => (

    ) ) }

    so it expect this strucuture:
    but what it get is all the slide inside swiper-wrapper div so in place of the closing tag of swiper-wrapper it gets the opening tag of div class=”swiper-slide”. Is there a workaround to avoid this block validation error ?
    Thank you

    • The problem comes more from the “save” function, you probably forgot to declare an attribute or you’re not parsing an attribute properly. (source).

    • Ah so maybe it comes from the query attributes. I declare them like that : const {src,link,id,caption,alt} = attributes.images;
      It seems that the code didn’t pass to comment so here is the codepen link : https://codepen.io/anon/pen/pLOGKa
      but maybe it comes the fact I use the save function. Maybe I had to create a php file instead and return null for the save function like latest post block

  25. Any ideas on how to access gutenberg block data inside an external javascript file for example setting the speed of a slider or something like that?

    • The simplest solution is to save this attribute in a data attribute in your save function.
      `data-speed={ myAttribute }`

    • I wonder if the save function works like a javascript temple like Mustache
      and how you add data inside a string for example for data-attributes='{“slidesToShow”:3,”data-speed”:600}’
      I guess this wouldn’t work
      data-attributes='{“slidesToShow”:{ number },”data-speed”:{ speed}}’

      • The save function works like any React functional component, you can add data attributes like you’d add them in any React element `data-slides={ myNumber } data-speed={ mySpeed }`…

    • I don’t think at the moment, you could try to use the wp.media API your self to build something similar to the `wp.blocks.MediaUpload` component. (Or you can propose an improvement in the Gutenberg repository)

  26. Good stuff Riad!
    I’m trying to figure out how to add a data-attributes to an existing block but I’m running into a few snags.
    Any advice you could point out for me would be super appreciated!

  27. Hi Riad,

    First of all, thanks for this post, it’s been very helpful for me.

    Just a question, I need to loop through an array fetched using the withAPIData component:

    [{“slug”:”primary”,”description”:”Main Menu”},
    {“slug”:”social”,”description”:”Social Menu”}]

    I’d like to populate a SelectControl component with this data. I’m using the lodash map function for this:

    ( {
    value: slug,
    label: description,
    } ) ) }
    onChange={ this.setMenu }
    />

    But I’m unable to make it work. The select is filled up with two options (this is correct), but the value and label are not filled up with the correct data.

    Any help would be appreciated.

    Thanks.

  28. It seems that some code got stripped from my previous comment.
    This is the code with the less than and greater than signs replaced:

    [SelectControl
    label={ __( ‘Menu’ ) }
    value={ attributes.menu }
    options={ map( menus, ( slug, description ) => ( {
    value: slug,
    label: description,
    } ) ) }
    onChange={ this.setMenu }
    /]

    • Did you try `console.log( menus )`? Where does the `menus` variable come from?

      • This is the code:

        edit: withAPIData( ( props ) => {
        return {
        menus: ‘/menus/v1/menus’ // custom REST API endpoint
        };
        } ) ( ( props ) => {
        const attributes = props.attributes;
        const menus = props.menus.data;
        (… rest of the code omitted…)
        } )

        And console.log( menus ) returns:

        [{“slug”:”primary”,”description”:”Main Menu”},
        {“slug”:”social”,”description”:”Social Menu”}]

      • Replace `map( menus, ( slug, description )` with `map( menus, ( { slug, description } )`

Discover more from Riad Benguella

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

Continue reading