How to extend the gallery block in Gutenberg?

Ok, I’ve been playing with this for a little bit and have managed to change the output of the Gallery block, with the following caveats:

  • The preview does not match the output. I think this is possible but appears to be a bit more involved.
  • Certain classes and markup are required in the output for the block to be able to parse the content and keep it editable. These classes have front-end styles that will need to be dealt with. I’m not sure at this point if there is a way to filter how the block does this. If it were possible it might not even be a good idea if it means Gallery blocks are broken when a theme or plugin is deactivated. A totally new block would probably be the way to go for situations where this would be required.
  • I’m not really sure how image sizes work at this stage.
  • The method of JavaScript hooks used might not be relevant in the final release. Gutenberg uses @wordpress/hooks while discussion about what hooks system to use in Core is ongoing.
  • Since the output of Blocks is saved as HTML, not a shortcode or meta, it won’t be possible to modify the output of existing Galleries without editing them.

The first thing we need to do is register a script. This is done with wp_enqueue_scripts(), but on the enqueue_block_editor_assets hook.

The script should have the wp-blocks script as a dependency. It is almost certainly already loaded in the editor, but making it a dependency presumably ensures it is loaded before our script.

function wpse_298225_enqueue_block_assets() {
    wp_enqueue_script(
        'wpse-298225-gallery-block',
        get_theme_file_uri( 'block.js' ),
        ['wp-blocks']
    );
}
add_action( 'enqueue_block_editor_assets', 'wpse_298225_enqueue_block_assets' );

The HTML for a block’s output is handled by the save() method of the block. You can see the Gallery block’s in this file.

At this stage (March 2018) Gutenberg supports a filter on the save method of blocks, blocks.getSaveElement. We can add a hook to this in JavaScript like this:

wp.hooks.addFilter(
    'blocks.getSaveElement',
    'wpse-298225',
    wpse298225GallerySaveElement
)

The first argument is the hook name, the 2nd argument is – I think – a namespace, and the last argument is the callback function.

Since we are replacing the save() method of the block, we need to return an new element. However, this is not a normal HTML element we need to return. We need to return a React element.

When you look at the original block’s save() method what you see is JSX. React, which Gutenberg uses under-the-hood, supports it for rendering elements. See this article for more on that.

JSX normally requires a build step, but since I’m not introducing a build step for this example, we need a way to create an element without JSX. React provides this with createElement(). WordPress provides a wrapper for this and other react functionality in the form of wp.element. So to use createElement() we use wp.element.createElement().

In the callback function for blocks.getSaveElement we get:

  • element The original Element created by the block.
  • blockType An object representing the block being used.
  • attributes The properties of the block instance. In our example this includes the images in gallery and settings like the number of columns.

So our callback function needs to:

  • Return the original element for non-block galleries.
  • Take the attributes, particularly the images, and create a new React element out of them representing the gallery.

Here is a complete example that simply outputs a ul with a class, my-gallery, and lis for each image with the class my-gallery-item and and img in each one with the src set to the image URL.

function wpse298225GallerySaveElement( element, blockType, attributes ) {
    if ( blockType.name !== 'core/gallery' ) {
        return element;
    }

    var newElement = wp.element.createElement(
        'ul',
        {
            'className': 'wp-block-gallery my-gallery',
        },
        attributes.images.map(
            function( image ) {
                return wp.element.createElement(
                    'li',
                    {
                        'className': 'blocks-gallery-item my-gallery-item',
                    },
                    wp.element.createElement(
                        'img',
                        {
                            'src': image.url,
                        }
                    )
                )
            }
        )
    )

    return newElement
}

Some things to take note of:

  • The original gallery block finds images by looking for ul.wp-block-gallery .blocks-gallery-item, so this markup and those classes are required for editing the block to be possible. This markup is also used for the default styling.
  • attributes.images.map is looping over each image and returning an array of child elements as the content for the main element. Inside these elements there is another child element for the image itself.

Leave a Comment