There's plenty of tutorials on creating your own custom Gutenberg blocks, but I found that the between beginner and advanced was lacking. I'm going to skip the basics but rather a short list of things to understand to more effectively work with Gutenberg. So from a trial-by-fire experience of working on two Wordpress 5.0 Gutenberg websites, here's what I've learned. The guiding principal is to re-use functionality when possible and try to replicate the Wordpress UI.

Rule 0: Understand React's role

Wordpress chose to use React to create it's UX for its new Gutenberg block editor. However, instead of using React directly, Wordpress uses Element, an abstraction layer over React. If you're wondering why someone would want to do this, Wordpress has a very concise list:

  • In many applications, especially those extended by a rich plugin ecosystem as is the case with WordPress, it’s wise to create interfaces to underlying third-party code. The thinking is that if ever a need arises to change or even replace the underlying implementation, it can be done without catastrophic rippling effects to dependent code, so long as the interface stays the same.
  • It provides a mechanism to shield implementers by omitting features with uncertain futures (createClass, PropTypes).
  • It helps avoid incompatibilities between versions by ensuring that every plugin operates on a single centralized version of the code.

This means you'll be writing code and making imports to wp.element, wp.components and wp.blocks. React only exists in the admin side of things, and all the content found within a block is saved statically meaning you won't be able to create React experiences simply by creating Gutenberg blocks. The



Rule 1: Bundle your custom blocks in one plugin

It's pretty easy to bundle all your custom blocks into one plugin. Unless you're looking to distribute your custom blocks across many sites, it's saner for development and deployment to make one master plugin for all your blocks. On the dev side, this means a single webpack instance to spin up as opposed to one for each block. A good example of this in action is Zac Gordon's WPForJSCourse example. While his course isn't free, this plugin is. This includes everything you need: A sane structure, a webpack config and a setup. All the custom blocks are registered in one nifty index js file.

It's pretty easy to do, but it was a design pattern that I didn't realize would benefit me when I started in on Gutenberg.



Rule 2: Learn Innerblocks and reuse core blocks

Most tutorials seem to stop short of innerblocks, and Innerblocks are probably one of the most important features of Gutenberg. Innerblocks allow you to load blocks inside of blocks. Below is a super basic example for a slideshow, allowing the user to enter as many images using the core/image Gutenberg block but restricting the user from entering any more.

    edit: props => {
        const { attributes: {selectControl},
            className, setAttributes, isSelected, } = props;
            const ALLOWED_BLOCKS = [ 'core/image' ];
        return [
            div className="slideshow-super-simple">
            <strong>Note: </strong> all slides are visible in editor<br />
          <InnerBlocks
            allowedBlocks={ ALLOWED_BLOCKS }
          />
          </div>
        ];
    },
    save: props => {
        const { attributes: { selectControl } } = props;
        return (
            <div className="icon-simple-slideshow" >
              <div className={ selectControl} ><InnerBlocks.Content /></div></div>
        );
    },
    

Innerblocks aren't simply limited to allowing and restricting other blocks, they can also accept templates, which is a set of pre-defined blocks. This allows assembling a very complicated UI or layout widget out of any number of prebuilt or custom blocks. There's no reason to reinvent the wheel as Wordpress gives you quite a few different blocks. Below I've included a list of all the common blocks by category.

Common blocks category
  • core/paragraph
  • core/image
  • core/heading
  • (Deprecated) core/subhead — Subheading
  • core/gallery
  • core/list
  • core/quote
  • core/audio
  • core/cover (previously core/cover-image)
  • core/file
  • core/video
Formatting category
  • core/table
  • core/verse
  • core/code
  • core/freeform — Classic
  • core/html — Custom HTML
  • core/preformatted
  • core/pullquote
Layout Elements category
  • core/button
  • core/text-columns — Columns
  • core/media-text — Media and Text
  • core/more
  • core/nextpage — Page break
  • core/separator
  • core/spacer
Widgets category
  • core/shortcode
  • core/archives
  • core/categories
  • core/latest-comments
  • core/latest-posts
  • core/calendar
  • core/rss
  • core/search
  • core/tag-cloud
Embeds category
  • core/embed
  • core-embed/twitter
  • core-embed/youtube
  • core-embed/facebook
  • core-embed/instagram
  • core-embed/wordpress
  • core-embed/soundcloud
  • core-embed/spotify
  • core-embed/flickr
  • core-embed/vimeo
  • core-embed/animoto
  • core-embed/cloudup
  • core-embed/collegehumor
  • core-embed/dailymotion
  • core-embed/funnyordie
  • core-embed/hulu
  • core-embed/imgur
  • core-embed/issuu
  • core-embed/kickstarter
  • core-embed/meetup-com
  • core-embed/mixcloud
  • core-embed/photobucket
  • core-embed/polldaddy
  • core-embed/reddit
  • core-embed/reverbnation
  • core-embed/screencast
  • core-embed/scribd
  • core-embed/slideshare
  • core-embed/smugmug
  • core-embed/speaker
  • core-embed/ted
  • core-embed/tumblr
  • core-embed/videopress
  • core-embed/wordpress-tv
Dummy Image

Pictured: Mock up of a hypothetical user page

Let's break down the above design: It's two columns consisting of:

Column 1 (core/column) Column 2 (core/column)
Image (core/image) Headline (custom)
Sub-Headline (custom)

Paragraph (custom)

With Gutenberg, simple layouts like the above can potentially be done using the code block, but it isn't desirable as it requires a bit of mastery of Wordpress, with a high margin of error if it taps into using custom CSS. We have a two column design, consisting of an Image in the first column, and two fields in the second, followed by text beneath the columns. So let's look a template code.

     edit: props => {
            const { attributes: { paragraph },
                className, setAttributes, isSelected } = props;
                const TEMPLATE = [
                  [ 'core/columns', {columns: 2,className: "profile-outer-column"}, [
                      [ 'core/column', { className: "profile-inner-column" }, [
                        ['core/image', { className: "profileImage"}],
                      ], ],
                      [ 'core/column', {className: "profile-inner-column"}, [
                        ['mycustomblocks/profile-title', { className: "profileTitle"}],
                        ['mycustomblocks/profile-name', {  className: "profileName"}]
                      ],],
                  ],],
                  ['mycustomblocks/profile-bio', { className: "profileBio"}]

                ];
            return [
                <div className={ className + " my-profile-editor"}>
                  <InnerBlocks template={TEMPLATE} />
                </div>
            ];
        },
        save: props => {
              const { paragraph,className } = props.attributes;
            return (
                <div className={className + " my-profile"}><InnerBlocks.Content  /></div>
            );
        },
   

Using the templates, I'm able to place inside columns a mixture of custom and factory Gutenberg blocks! Innerblocks aren't infallible, you can template lock blocks so users cannot add more blocks but occasionally this creates issues. Also, the custom styling for blocks does not work on any block that features an innerblock (yet). Perhaps this will change but as of writing this, it hasn't.



Rule 3: Restricting dependent blocks

Often you'll create a block that shouldn't appear in your list of plugins from the Wordpress gutenberg GUI. Any custom block can be easily restricted to being only accessible from a certain block type. In my previous example I had two custom blocks, mycustomblocks/profile-title and mycustomblocks/profile-name. These two blocks are very simple blocks, but I do not want them polluting my list of Gutenberg blocks. This only requires declaring the parent

export default registerBlockType(
    'mycustomblocks/profile-title',
    {
        title: __( 'Profile Title', 'mycustomblocks' ),
        description: __( 'This field is for the user profile's job title.', 'mycustomblocks'),
        category: 'common',
        keywords: [
            __( 'text', 'mycustomblocks' ),
            __( 'MediaUpload', 'mycustomblocks' ),
            __( 'Message', 'mycustomblocks' ),
        ],
        parent: ['mycustomblocks/slideshow-slide'],

See the parent flag? It really is that easy.



Rule 4: Learn to use the custom toolbar and form fields

To truly make a plugin feel native, you'll need to tap into the UX that Gutenberg uses, the toolbar and the sidebar containing form fields, using InspectorControls and BlockControls. Again, Zac Gorden's JSForWordpress tutorial repo has a great example of each.

InspectorControls appear in the sidebar of a block, zgordon also has a nice tutorial on it as well as Eudes' medium post. Also, be sure to see the official documentation on InspectorControls.

          <InspectorControls>
              <PanelBody
                  title={ __( 'High Contrast', 'jsforwpblocks' ) }
              >
                  <PanelRow>
                      <label
                          htmlFor="high-contrast-form-toggle"
                      >
                          { __( 'High Contrast', 'jsforwpblocks' ) }
                      </label>
                      <FormToggle
                          id="high-contrast-form-toggle"
                          label={ __( 'High Contrast', 'jsforwpblocks' ) }
                          checked={ highContrast }
                          onChange={ toggleHighContrast }
                      />
                  </PanelRow>
              </PanelBody>
          </InspectorControls>

BlockControls appear in the editable area of a block as inline controls. Also, be sure to see the official documentation on Toolbars and Inspector

<BlockControls>
    <AlignmentToolbar
        value={ textAlignment }
        onChange={ ( textAlignment ) => props.setAttributes( { textAlignment } ) }
    />
    <Toolbar>
        <Tooltip text={ __( 'High Contrast', 'jsforwpblocks' )  }>
            <Button
                className={ classnames(
                    'components-icon-button',
                    'components-toolbar__control',
                    { 'is-active': highContrast },
                ) }
                onClick={ toggleHighContrast }
            >
                {icons.contrast}
            </Button>
        </Tooltip>
    </Toolbar>
</BlockControls>

It will feel a little strange but the edit return is returned as an array, for the main editable area mark up.