How to Create A Custom Block Without Plugin (Updated 2020)

Creating a custom Gutenberg block isn't an easy feat especially if you are not comfortable with JavaScript. We will you guide you slowly through the process

This article has been updated to reflect the standard way in latest WordPress version.

Creating a custom block isn’t an easy feat especially if you are not comfortable with pure JavaScript. Hopefully this guide can help you with that.

Before Making Custom Block

Is there an existing block that fits your need?

First, ask that question to yourself before wasting time to create redundant block.

For example I once need to create this design in Gutenberg:

How do you create this layout in Gutenberg?

Native block that fits the above is Media-Text. The part that we can’t create natively is the Colored box behind the image and the stars below the Heading.

So we simply create custom styles:

register_block_style( 'core/media-text', [ 'name' => 'half-bg', 'label' => 'Half Background' ] );

register_block_style( 'core/heading', [ 'name' => 'has-stars', 'label' => 'Has Stars' ] );

Now we can use pseudoselector ::before and ::after to create that design.

If none of the existing blocks fits your need, continue the tutorial below.

1. Register Assets & Block Type

Create asset files (CSS & JS), register them, then enqueue them using register_block_type() function.

Block name’s format must be namespace/block-id.

add_action( 'init', 'my_register_block_type' );

function my_register_block_type() {
  // if Gutenberg is not active
  if ( !function_exists( 'register_block_type' ) ) {
    return;
  }

  // Register assets
  $css_dir = get_stylesheet_directory_uri() . '/assets/css';
  $js_dir = get_stylesheet_directory_uri() . '/assets/js';
  wp_register_script( 'my-block-admin', $js_dir . '/my-block-admin.js', [ 'wp-blocks', 'wp-dom' ] , null, true );
  wp_register_style( 'my-block-admin', $css_dir . '/my-block-admin.css', [ 'wp-edit-blocks' ] );
  wp_register_style( 'my-block', $css_dir . '/my-block.css' );

  // Register block type and enqueue the assets.
  register_block_type( 'my/simple-text', [
    'editor_style' => 'my-block-admin',  // admin only
    'editor_script' => 'my-block-admin', // admin only
    'style' => 'my-block',               // admin & public
  ] );
}

2. The Basic Script

The snippet below is a starting point for creating a custom block:

( function( blocks, element, blockEditor ) {
  var el = element.createElement;
  var RichText = blockEditor.RichText;

  blocks.registerBlockType( 'my/simple-text', {
    title: 'Simple Text (Custom)',
    icon: 'universal-access-alt',
    category: 'layout',
    attributes: {},
    edit: myEdit,
    save: mySave
  } );
  
  // what's rendered in admin
  function myEdit( _props ) {
  
  }
  
  // what's saved and rendered in frontend
  function mySave() {
    
  }

}( window.wp.blocks, window.wp.element, window.wp.blockEditor ) );

Let’s take a deeper look at some of registerBlockType() parameters:

  • iconClick here for a list of available icons.
  • attributes – Defines how to extract the value from saved content. More info»
  • edit – Callback on how to render the content in the editor.
  • save – Callback on how to render the content before saved to the database.

3. Implementing Attributes, Edit, & Save

( function( blocks, element, blockEditor ) {
  var el = element.createElement;
  var RichText = blockEditor.RichText;

  blocks.registerBlockType( 'my/simple-text', {
    title: 'Simple Text (Custom)',
    icon: 'universal-access-alt',
    category: 'layout',
    example: {},

    // All the children inside 'p' will be extracted as 'text' variable.
    attributes: {
      text: { type: 'array', source: 'children', selector: 'p' }
    },
    edit: myEdit,
    save: mySave,
  } );

  // Render RichText (field with basic toolbar) wrapped with 'p'
  function myEdit( props ) {
    return el( RichText, {
      tagName: 'p',
      onChange: _onChange,
      value: props.attributes.text,
      className: props.className,
    } );
    
    // when changed, update the attribute
    function _onChange( newContent ) {
      props.setAttributes( { text: newContent } );
    }
  }
  
  // Save the content of RichText wrapped with 'p'
  function mySave( props ) {
    return el( RichText.Content, {
      tagName: 'p',
      className: props.className,
      value: props.attributes.text,
    } );
  }
  
}( window.wp.blocks, window.wp.element, window.wp.blockEditor ) );

4. Check the Result

Selecting the custom block
Our “Simple Text” custom block in action

Conclusion

Creating a custom block is difficult and can get ugly. So always try to just use custom style if possible.

If you are interested to learn more, we made an extensive tutorial over on our Github:

Feel free to leave a question below regarding custom block 🙂

Default image
Henner Setyono
A web developer who mainly build custom theme for WordPress since 2013. Young enough to never had to deal with using Table as layout.
Leave a Reply

5 Comments

  1. Shouldn't the path to the file in point 2 and 3 be '/assets/js/my-block-admin.js' - same as enqued scripts file?

  2. Mobile Image for Cover Block (Gutenberg) | WP Tips

    […] The JS is long and complex, so copy it from our gist. If you want to learn the syntax, check out our other tutorial. […]

  3. I literally copied and pasted and it's not working on my website.

    Is there something else I need to do?