Reading list Switch to dark mode

    How to create a custom Gutenberg Block in WordPress

    Updated 18 September 2023

    WordPress recently introduced @wordpress/scripts npm package. It is a collection of reusable scripts for WordPress Development. We will learn about how to create a Custom Gutenberg block using ES6 , JSX and @wordpress/scripts package.
    Since most browsers cannot support or run ES6 and JSX, we need to use tools Babel to transform these syntaxes, to the code that browsers can understand. Also by using a build tool like Webpack, we can organize our code into smaller modules and then bundle them together into a single download.

    The @wordpress/scripts package abstracts these libraries away to standardize and simplify development, so you won’t need to handle the details for configuring those libraries.

    Before Starting Development

    • We need WordPress development environment
    • JavaScript build tools (node/npm) if using React/JSX

    Create directory for Gutenberg Block in plugins directory

    Create a new directory in wordpress/wp-content/plugins with name myFirst-block and install @wordpress/scripts package using npm inside of it. The — save-exact installs the exact version of the @wordpress/scripts package

    cd wordpress/wp-content/plugins
    mkdir myFirst-block
    cd myFirst-block
    npm init
    npm i --save-dev --save-exact @wordpress/scripts

    This will install all required packages and dependencies inside a node_modules directory. Please note that src/index.js will be its entry point and output build file will be in build/index.js

    So also create two directories in myFirst-block directory as src and build

    Searching for an experienced
    WordPress Company ?
    Read More

    This is a CLI and exposes a binary called wp-scripts so we can call it directly. So we will add these script in the package.json

    "scripts": {
      "start": "wp-scripts start",
      "build": "wp-scripts build"

    When we run

    npm run start then wp-scripts will run in watch mode So if we make changes in src/index.js then it will automatically create build file for it real time

    Create plugin file to Register Block in plugin

    We need to register our block server-side to ensure that the script is enqueued when the editor loads, Now we create a plugin file to enquee styles and scripts build/index.js to our block

    Plugin Name: myFirst-block
    Author: webkul
    function myFirst-block() {
    	$styleURI = plugin_dir_url( __FILE__  ).'/src/style.css';
    	//Enquee style
    	// Register JavasScript File build/index.js
    		plugins_url( 'build/index.js', __FILE__ ),
    		array( 'wp-blocks', 'wp-element', 'wp-editor' ),
    	// Register editor style src/editor.css
    		plugins_url( 'src/editor.css', __FILE__ ),
    	// Register block
    	register_block_type( 'gutenreact/myFirst-block', array(
    		'editor_script' => 'myFirstblock',
    		'editor_style' => 'myFirst-block-editor-style',
    	) );
    add_action( 'init', 'myFirst-block' );

    Now we need to create index.js file in src directory, this will be our entry point.

    In index.js we can register a block with registerBlockType() function, registerBlockType will take two arguments.

    1. Block name : This is a unique identify string for our Block.
    2. Object(Array) : This is a configuration object of our Block. Object [ { key: value } ]
    // Registering my first block with a unique name
    registerBlockType( 'myCustomBlock/my-first-block', {} );

    namespace is the name of our theme or plugin.
    This name is used on the comment delimiters as <!-- wp:namespace/block-name --> when the post is saved into the database.

    Now we are going to register our first block using registerBlockType() function. Here myFirst-block is my plugin directory my-first-block is my block name. Now we need to put the title, description, icon, and category, Here myCustomBlock is my namespace of plugin.

    The edit function is use for editor.

    The save function is for frontend and define how the attributes will combine and render a final markup.

    Lets bundle it

    Create css and js file in myFirst-block/src for styles and scripts.

    .wp-block-myCustomBlock-my-first-block {
        color: cornflowerblue;
    .wp-block-myCustomBlock-my-first-block {
        color: tomato;

    Our block will shown in gutenberg modules

    gutenberg custom block

    Create advance block for card component

    We are going to use useBlockProps React hook on the block wrapper element, useInnerBlocksProps for adding default components of gutenbrg into our custom block like heading, paragraph, buttons etc and MediaUpload for card Image.

    Here we will use useInnerBlocksProps for giving support of adding other Blocks inside our custom Block ex: Button, Text, Heading, etc.

    // src/index.js
    import { registerBlockType } from '@wordpress/blocks';
    import { useBlockProps, RichText, MediaUpload,  InnerBlocks } from '@wordpress/block-editor';
    registerBlockType('gutenreact/webkul-cards', {
        title: 'Webkul Card Section',
        icon: 'columns',
        category: 'widgets',
        attributes: {
            content: {
                type: 'string',
                source: 'html',
                selector: 'p',
            description: {
                type: 'string',
                source: 'html',
                selector: 'p',
            imgUrl: {
                type: 'string',
        edit: (props) => {
            const {
                attributes: { content, description, imgUrl },
            } = props;
            const blockProps = useBlockProps();
            const onChangeContent = (newContent) => {
                setAttributes({ content: newContent });
            const onChangeDescription = (newDescription) => {
                setAttributes({ description: newDescription });
            const onImageSelect = (newImgUrl) => {
                setAttributes({ imgUrl: newImgUrl.sizes.full.url });
            const ALLOWED_BLOCKS = [ 'core/buttons' ];
            return (
                <div className='webkul-card'>
                    <MediaUpload onSelect={onImageSelect}
                            ({ open }) => {
                                return imgUrl ? <img onClick={open} src={imgUrl} /> : <button onClick={open}> Upload Image </button>
                        title="Insert Image"
                    <RichText {...blockProps}
                        value={content} />
                    <RichText {...blockProps}
                        placeholder='Short description'
                        value={description} />
                    <InnerBlocks allowedBlocks={ ALLOWED_BLOCKS } />
        save: (props) => {
            const blockProps =;
            return (
                <div className='webkul-card'>
                    <img src={props.attributes.imgUrl} />
                    <RichText.Content {...blockProps}
                        value={props.attributes.content} />
                        <RichText.Content {...blockProps}
                        value={props.attributes.description} />
                     <InnerBlocks.Content />

    We can also have a validator function ALLOWED_BLOCKS in which we can allow only selected innerBlocks to add in our custom Block

    const ALLOWED_BLOCKS = [ 'core/image', 'core/paragraph' ];
    <InnerBlocks allowedBlocks={ ALLOWED_BLOCKS } />;

    Here we give support of Image and Paragraph to our custom Block, you can give more Blocks. you can check core blocks here.

    Lets bundle it

    npm run start

    Now our card block is appear in gutenberg editor

    Now we write some css for frontend and editor

    // src/editor.css
    .webkul-card {
        text-align: center;
        background: #d5d5d5;
        padding: 40px 20px;
        border: 1px solid #000000;
    //  src/style.css
    .webkul-card {
        text-align: center;
        background: #d5d5d5;
        padding: 40px 20px;

    If you need custom WordPress Development services then feel free to reach us and also explore our exclusive range of WordPress WooCommerce Extensions.

    !!Have a Great Day Ahead!!

    . . .
    Discuss on Helpdesk

    Leave a Comment

    Your email address will not be published. Required fields are marked*

    Be the first to comment.

    Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home

    Table of Content