Search code examples
phpreactjswordpresswordpress-themingwordpress-gutenberg

Getting 'Block "my-plugin/block-name" is already registered' in Gutenberg editor


I am trying to create a plugin that will have multiple blocks and an admin page. Everything seems to be working fine. The admin page is showing up, the blocks are working as well. But I am getting an error in the console when I get to the Gutenberg editor and that is making me worried if what I did is wrong or not.

This is the error:

'Block "my-plugin/block-1" is already registered' 'Block "my-plugin/block-2" is already registered'

This is the plugin php file:

class MY_PLUGIN{
    public function __construct() {
        add_action( 'admin_menu', [ $this, 'menu_item' ] );
        add_action( 'admin_enqueue_scripts', [ $this, 'load_custom_wp_admin_scripts' ] );
        add_action( 'init', [ $this, 'register_blocks' ] );
    }

    /**
    * Initialize the plugin
    */
    public static function init(){
        static $instance = false; 
        if( !$instance ) {
            $instance = new self();
        }
        return $instance;
    }

    /**
    * Create a new admin page for our app.
    */
    public function menu_item() {
        add_menu_page(
            'My Plugin',
            'My Plugin',
            'manage_options',
            'my-plugin',
            [ $this, 'dashboard_page' ],
            'dashicons-schedule',
            3
        );
    }

    /**
    * Admin page markup.
    */
    public function dashboard_page() {
        echo '
            <h2>Pages</h2>
            <div id="my-plugin"></div>
        ';
    }

    /**
    * Load admin scripts and stylesheets
    */
    public function load_custom_wp_admin_scripts( $hook ) {
        // Load only on ?page=my-plugin
        if ( 'toplevel_page_my-plugin' !== $hook ) {
            return;
        }

        // Load the required WordPress packages.

        // Automatically load imported dependencies and assets version.
        $asset_file = include plugin_dir_path( __FILE__ ) . 'build/index.asset.php';

        // Enqueue CSS dependencies.
        foreach ( $asset_file['dependencies'] as $style ) {
            wp_enqueue_style( $style );
        }

        // Load our app.js.
        wp_register_script(
            'my-plugin',
            plugins_url( 'build/index.js', __FILE__ ),
            $asset_file['dependencies'],
            $asset_file['version']
        );
        wp_enqueue_script( 'my-plugin' );

        // Load our style.css.
        wp_register_style(
            'my-plugin',
            plugins_url( 'src/admin/stylesheet/style.css', __FILE__ ),
            array(),
            $asset_file['version']
        );
        wp_enqueue_style( 'my-plugin' );
    }

    public function register_blocks() {
        register_block_type( __DIR__ . '/build/blocks/block-1' );
        register_block_type( __DIR__ . '/build/blocks/block-2' );
   }
}

Each block's index.js file looks like this:

import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import './editor.scss';
import Edit from './edit';
import Save from './save';
import metadata from './block.json';

/**
 * Every block starts by registering a new block type definition.
*/
registerBlockType( metadata, {
    edit: Edit,
    save: Save,
} );

Am I doing anything wrong here?

UPDATE* Here's how block.json looks like. Both block's the same except name and title is different in each. Like block-1 and block-2:

{
    "$schema": "https://schemas.wp.org/trunk/block.json",
    "apiVersion": 2,
    "name": "my-plugin/block-1",
    "version": "0.1.0",
    "title": "Block 1",
    "category": "text",
    "icon": "flag",
    "description": "Some description",
    "attributes": {
        "message": {
            "type": "string",
            "source": "text",
            "selector": "div"
        }
    },
    "supports": {
        "html": false
    },
    "textdomain": "block-1",
    "editorScript": "file:../../index.js",
    "editorStyle": "file:../../index.css",
    "style": "file:../../style-index.css"
}

FOLDER STRUCTURE:

enter image description here

Admin Index.js:

import { render }                   from '@wordpress/element';
import App                          from './components/App/App';

window.addEventListener(
    'load',
    function () {
        render(
            <App />,
            document.querySelector( '#my-plugin' )
        );
    },
    false
);

UPDATE:

Getting these errors in admin side and blocks are missing after adding custom webpack file as suggested by S.Walsh.

enter image description here

UPDATE:

After implementing S.Walsh's suggestions, the blocks were working fine but admin page was not showing any components. So I updated load_custom_wp_admin_scripts function to build path for the register script:

public function load_custom_wp_admin_scripts($hook) {
    if ('toplevel_page_my-plugin' !== $hook) {
        return;
    }

    $asset_file = include plugin_dir_path(__FILE__) .'build/admin/index.asset.php';

    foreach ( $asset_file['dependencies'] as $style ) {
        wp_enqueue_style( $style );
    }

    wp_register_script('my-plugin', plugins_url('build/admin/index.js', __FILE__), $asset_file['dependencies'], $asset_file['version']);
    wp_enqueue_script('my-plugin');

    wp_register_style('my-plugin', plugins_url('src/admin/stylesheet/style.css', __FILE__), array(), $asset_file['version']);
    wp_enqueue_style('my-plugin');
}

The components are showing up now but the stylesheet in not taking any effect.

UPDATE:

For the admin styles to take effect, the css file should be changed to scss. And also update it in load_custom_wp_admin_scripts function. I also removed view.js files as it is not needed.


Solution

  • The error is triggered in plugin.php by the function load_custom_wp_admin_scripts() as:

    1. The file src/index.js contains code to register the blocks on the admin_enqueue_scripts hooks in load_custom_wp_admin_scripts().

    2. The blocks are also registered via the register_blocks() function using each blocks block.json on the init hook.

    When you visit your custom Admin page, the admin_enqueue_scripts and init hook both attempt to register the blocks, creating the error.

    Your screenshot shows that the script entry point is compiling all the assets into index.asset.php hence your blocks build directories only contains the block.json files without their assets.

    The best practise is to keep the block code independent of any other plugin JavaScript, for this kind of project setup, you can define multiple entry points to build the required assets.

    Solution

    Let's set up a basic plugin project with multiple blocks from the start to explore an alternative approach:

    1. Project Setup via command line with @wordpress/create-block:

    # 1. Create "my-plugin" and install required files
    npx @wordpress/create-block@latest my-plugin
    
    # 2. Remove the default src directory that contains the default single block
    rmdir /s my-plugin\src
    
    # 3. Make a new src\blocks directory and enter it
    mkdir my-plugin\src\blocks && cd my-plugin\src\blocks
    
    # 4. Use create block to scaffold new blocks as required with --no-plugin option
    npx @wordpress/create-block@latest --no-plugin block-1
    npx @wordpress/create-block@latest --no-plugin block-2
    
    # 5. Run build
    cd [path-to]\my-plugin
    npm run build 
    

    Update the create_block_my_plugin_block_init() function to register both blocks:

    my-plugin.php

    function create_block_my_plugin_block_init() {
        register_block_type( __DIR__ . '/build/blocks/block-1' );
        register_block_type( __DIR__ . '/build/blocks/block-2' );
    }
    add_action( 'init', 'create_block_my_plugin_block_init' );
    

    Test the plugin from the Editor to see both block-1 and block-2 load without error.

    At this stage, you have a solid foundation for a basic plugin with two working blocks, properly compiled with their assets.

    The next steps are optional to extend the plugin with an Admin page.

    2. Admin Page Setup

    The src/admin/ directory will contain a index.js script which imports the stylesheet/style.scss as its required for build process. The index.js does not load any blocks.

    src/admin/index.js

    import './stylesheet/style.scss';
    ...
    // Other code for Admin Page..
    

    3. Build Setup

    Create a new custom webpack.config.js to enable additional entry points for your project to build all required assets:

    [my-plugin]/webpack.config.js

    const defaultConfig = require('@wordpress/scripts/config/webpack.config');
    
    module.exports = {
        ...defaultConfig,
        entry: {
            'admin/index': './src/admin/',
            'blocks/block-1/index': './src/blocks/block-1/',
            'blocks/block-2/index': './src/blocks/block-2/'
        },
    };
    

    4. Updates to Main Plugin File Next, update my-plugin.php to include your original class MY_PLUGIN code with the following update to load_custom_wp_admin_scripts():

    my-plugin.php

    class MY_PLUGIN
    {
    ...
    
        /**
         * Load admin scripts and stylesheets
         */
        public function load_custom_wp_admin_scripts($hook)
        {
            // Load only on ?page=my-plugin
            if ('toplevel_page_my-plugin' !== $hook) {
                return;
            }
    
            // Register and load admin script with dependancies
            $asset_file = include plugin_dir_path(__FILE__) . 'build/admin/index.asset.php';
            wp_register_script('my-plugin-admin', plugins_url('build/admin/index.js', __FILE__), $asset_file['dependencies'], $asset_file['version']);
            wp_enqueue_script('my-plugin-admin');
    
            // Register and load admin style.css.
            $admin_style = plugins_url('build/admin/style-index.css', __FILE__);
            wp_register_style('my-plugin-admin-style', $admin_style, array(), '1.0'); // check
            wp_enqueue_style('my-plugin-admin-style');
        }
    ...
    }
    

    Check that all the file paths are correct then rebuild your project. The build directory for each block now only contains that blocks assets; the script and style assets in for the Admin page are output to build\admin\.

    Example Project Directory Setup

    Now you can go on and create as many new blocks as you like!

    Additional Notes The errors shown in the updated question screenshot indicate that:

    1. 'view.asset.php` is missing means that "view.js" is defined block.json but no "view.js" exists in your blocks directory or is a different filename.
    2. The same as above applies to "editorSrc" where "index.js" is missing or the filename/path doesn't match.

    Both are resolved by either adding the missing files or updating their declarations in block.json to correct path/filename for your project, eg:

        "editorScript": "file:./index.js",
        "viewScript": "file:./view.js"
    

    Ensure you rebuild the project and clear browser cache before testing again.