How to Create A Custom Block Without Plugin (Updated)

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

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. So before creating one, think whether we can achieve what we want with custom style in existing block.

Follow the steps below on how to create Gutenberg block with code:

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

That’s Too Complex, Any Plugin?

Yes, just like the old-saying in WordPress: “There’s a plugin for that”.

So far I have tried LazyBlock and ACF Pro. LazyBlock is easier to use but ACF Pro is much more customizable because you can use any ACF fields.

One thing I don’t like about both of them is how out-of-place they look compared to the native block.


Conclusion

Creating a custom block is difficult and can get ugly. So always try to repurpose an existing block if possible. Need a block for Namecard? Media-Text block might be a good replacement.

If you are interested to learn multi fields block, check out the official example in Github. We are basing this tutorial on that example too.

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