Distributing a Gutenberg Block as a Plugin

Contents

Introduction

Distributing a custom Gutenberg block as a WordPress plugin is an excellent way to share functionality, ensure maintainability, and streamline updates. Whether you’re an agency delivering bespoke blocks to clients or an open-source contributor sharing with the community, packaging your block in a plugin makes installation and updates seamless.

Why Distribute a Gutenberg Block as a Plugin

  • Ease of installation: Users install and activate like any other plugin from the dashboard.
  • Version management: WordPress handles updates automatically when hosted on WordPress.org or a private repository.
  • Modularity: Blocks are decoupled from themes, ensuring theme changes don’t break them.
  • Community reach: Publishing publicly increases adoption and feedback.

Prerequisites

  • Node.js and npm or Yarn installed
  • WordPress development environment (Local by Flywheel, Docker, VVV, etc.)
  • Basic familiarity with PHP, JavaScript (ESNext), React and the Gutenberg API
  • WP-CLI (optional but recommended for scaffolding)

1. Plugin Boilerplate Folder Structure

Create a new folder under wp-content/plugins/my-block-plugin with the following structure:

  • my-block-plugin.php ndash main plugin file
  • block.json ndash block metadata
  • src/ ndash ESNext source files
  • build/ ndash compiled JS and CSS
  • README.txt ndash WordPress.org readme

1.1 Main Plugin File

ltphp
  /
    Plugin Name: My Custom Block
    Plugin URI:  https://example.com/my-custom-block
    Description: A reusable Gutenberg block packaged as a plugin.
    Version:     1.0.0
    Author:      Your Name
    Text Domain: my-custom-block
    Domain Path: /languages
    License:     GPLv2 or later
   /
  
  defined( ABSPATH )  exit

  function mcb_register_block() {
    register_block_type( __DIR__ )
  }
  add_action( init, mcb_register_block )

2. Defining block.json

block.json centralizes metadata and eliminates redundancy:

{
    apiVersion: 2,
    name: my-plugin/custom-block,
    title: Custom Block,
    category: widgets,
    icon: smiley,
    description: A custom Gutenberg block.,
    keywords: [ custom, block ],
    supports: {
      html: false
    },
    textdomain: my-custom-block,
    editorScript: file:./build/index.js,
    editorStyle: file:./build/editor.css,
    style: file:./build/style.css
  }

This instructs WordPress to load your compiled assets.

3. Developing the Block

3.1 Setting Up npm Scripts Dependencies

{
    name: my-custom-block,
    version: 1.0.0,
    scripts: {
      build: webpack --mode production,
      start: webpack --watch --mode development
    },
    devDependencies: {
      @wordpress/scripts: ^25.0.0,
      webpack: ^5.0.0,
      sass: ^1.32.0
    }
  }

Using @wordpress/scripts simplifies setup: it includes Babel, ESLint, webpack, and style loaders.

3.2 Writing the Block Code

Create src/index.js:

import { registerBlockType } from @wordpress/blocks
  import { __ } from @wordpress/i18n
  import Edit from ./edit
  import save from ./save
  import ./editor.scss
  import ./style.scss

  registerBlockType( my-plugin/custom-block, {
    edit: Edit,
    save
  } )

Edit Component (src/edit.js)

import { useBlockProps, RichText } from @wordpress/block-editor
  import { __ } from @wordpress/i18n

  export default function Edit( { attributes, setAttributes } ) {
    const blockProps = useBlockProps()
    return (
      ltdiv { ...blockProps }gt
        ltRichText
          tagName=p
          value={ attributes.content }
          onChange={ ( content ) =gt setAttributes( { content } ) }
          placeholder={ __( Write something…, my-custom-block ) }
        /gt
      lt/divgt
    )
  }

Save Component (src/save.js)

import { useBlockProps, RichText } from @wordpress/block-editor

  export default function save( { attributes } ) {
    const blockProps = useBlockProps.save()
    return (
      ltdiv { ...blockProps }gt
        ltRichText.Content
          tagName=p
          value={ attributes.content }
        /gt
      lt/divgt
    )
  }

4. Building Internationalization

  • Run npm run build to generate assets in build/.
  • Load translation files with load_plugin_textdomain(my-custom-block, false, dirname(__FILE__)./languages) in your main plugin file.
  • Provide .pot file via xgettext or Poedit.

5. Preparing for Distribution

5.1 Readme.txt for WordPress.org

Follow the readme.txt standard. Key sections:

  • Stable tag: version number
  • Requires at least: WP minimum version
  • Tested up to: WP maximum version
  • Changelog: version history

5.2 Packaging Versioning

  • Ensure plugin header is accurate (Version: 1.0.0).
  • Zip the folder or commit to Subversion for WordPress.org SVN.
  • Semantic versioning (MAJOR.MINOR.PATCH) for clarity.

6. Publishing on WordPress.org

  1. Request a plugin slug via Add New Plugin.
  2. Commit code to the SVN repository.
  3. Upload banner/icon in /assets for your plugin page.

Once approved, WordPress.org automatically processes updates when you commit new tags.

7. Continuous Integration Testing

  • Use @wordpress/scripts for linting and unit tests.
  • Set up GitHub Actions for npm test and npm run build on push.
  • Implement E2E tests via Jest and WordPress test environment.

8. Best Practices Security

  • Sanitize and escape all attributes in save functions.
  • Follow WordPress Coding Standards: WPCS.
  • Keep dependencies updated to minimize vulnerabilities.

9. Support Contribution

Provide a CONTRIBUTING.md file, outline issue templates, and use a public GitHub/GitLab repository. Encourage pull requests, report templates, and an issue triage process.

Conclusion

Distributing your Gutenberg block as a plugin maximizes its reach and simplifies maintenance. By adhering to WordPress coding standards, semantic versioning, and robust testing, you ensure a professional, secure, and future-proof package. Whether on WordPress.org or a private registry, your plugin can evolve gracefully—thanks to the framework you’ve set up.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

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