Skip to content
  1. Blog

Block Editor Development with HMR Support

Kelly Mears Kelly Mears on

bud.js has extensions that help with developing for the WordPress block editor, and as of v6.11.0, the @roots/wordpress-hmr library is included in @roots/bud-preset-wordpress, making it easy to register blocks and other editor specific code with hot reload (HMR) support.

While bud.js is included with our WordPress starter theme, it can be used independently of Sage, including on any sort of site or application that requires building front-end assets. Moreover, the wordpress-hmr package can be used independently of bud.js or even webpack.

bud.js vs create-block package

Bud does not currently support scaffolding new blocks like the official @wordpress/create-block package (we will in the future!), but it does offer an easier to use API for working with webpack over the wp-scripts package as well as an improved developer experience.

Building a block with @wordpress/create-block versus bud.js
Building a block with @wordpress/create-block versus bud.js

The biggest improvement is out-of-the-box support for hot module reloading, which persists block state when editing source code. We owe a debt of gratitude to K Adam White for the initial work he did on this problem, which was instrumental to crafting our solution.

Block editor development setup

The bud.js repo contains a wordpress-editor example as a helpful reference for getting started.

If you aren’t already using bud.js in your project, you might want to integrate it as a build tool for your WordPress plugin that contains your block related assets. You can reference Radicle for a working plugin with an example block and editor plugin that is built with bud.js.

Since blocks shouldn’t be included in your WordPress theme, the following setup is based on being configured from a directory for a plugin.

package.json

Create a package.json file in your plugin’s directory:

{
  "name": "example/wordpress-block",
  "private": true,
  "browserslist": [
    "extends @roots/browserslist-config"
  ],
  "scripts": {
    "build": "bud build",
    "dev": "bud dev"
  },
  "devDependencies": {
    "@roots/bud": "6.11.0",
    "@roots/bud-preset-wordpress": "6.11.0"
  }
}

bud.config.mjs

Create a bud.config.mjs file in your plugin’s directory:

/**
 * @param {import('@roots/bud').Bud} bud
 */
export default (bud) => {
  bud.entry(`editor`, `@src/index.js`)
};

src/index.js

Create a src/ directory along with an index.js file to load your block editor customizations:

roots.register.blocks(`./`)
roots.register.formats(`./`)
roots.register.variations(`./`)
roots.register.plugins(`./`)

if (import.meta.webpackHot) {
  import.meta.webpackHot.accept(console.error);
}

src/example.block.js

Add an example block to the src/ directory with a filename that ends in .block.js:

/* Block name */
export const name = `example/example-block-a`

/* Block title */
export const title = `Example block`

/* Block category */
export const category = `text`

/* Block edit */
export const edit = () => <></>

/* Block save */
export const save = () => <></>

/* Block styles */
export const styles = [
  { name: 'default', label: 'Default', isDefault: true },
  { name: 'custom', label: 'Custom' },
]

/* Block variations */
export const variations = [
  { name: `example/example-block-b`, title: 'Example block variant' }
]

At this point, you can run yarn && yarn build to build your assets for the first time.

Enqueuing the assets

Enqueue the editor assets in your WordPress plugin by using the following code:

if (! $manifest = realpath(__DIR__.'/dist/entrypoints.json')) {
    throw new \Exception('Example: you must run `yarn build` before using this plugin.');
}

$entrypoints = json_decode(file_get_contents($manifest));

add_action('wp_enqueue_editor', fn () => wp_enqueue_script(
    'example/editor',
    plugins_url('dist/js/editor.js', dirname(__FILE__)),
    $entrypoints->editor->dependencies,
    null,
    true,
), 100);

Getting HMR working for block editor development

To enable the use of HMR during dev mode (yarn dev), modify your Bud config to define the setProxyUrl and setUrl methods for the development server. In the following bud.config.mjs file, http://example.test is the URL to the working WordPress local development site, and http://localhost:3000 is the URL to access the Bud dev server:

export default (bud) => {
  bud
    .entry(`editor`, `@src/index.js`)
    .setUrl(`http://localhost:3000`)
    .setProxyUrl(`http://example.test`)
};

Future improvements

As mentioned at the beginning of this post, bud.js does not currently offer scaffolding tools for quickly creating new blocks and block related customizations (plugins, filters, formats, variations). We’ll be adding this functionality to bud.js in the future.