Importing Gutenberg core WordPress libraries as ES modules with webpack

Gutenberg comes with a set of core libraries which are necessary for any kind of custom block development. Namely those libraries are:

  • @wordpress/components: Generic, reusable UI WordPress components
  • @wordpress/i18n: Internationalization utilities
  • @wordpress/element: Abstraction on top of React
  • @wordpress/date: Date formatting and manipulation utilities
  • @wordpress/blocks: Module providing utilities for registering and building blocks
  • @wordpress/data: Abstraction on top of Redux
  • @wordpress/editor: Module representing the WordPress Editor’s page
  • @wordpress/utils: Various generic utilities

To use them you need to include them as external script requirements to your final build by registering and enqueueing them, similarly to how you’d include any kind of script or style within WordPress.

function my_plugin_enqueue_editor_assets() {
	wp_enqueue_script( 'myplugin', untrailingslashit( MY_PLUGIN_DIR_URL ) . '/build/myplugin.build.js', array(
		'wp-components',
		'wp-blocks',
		'wp-element',
		'wp-data',
		'wp-date',
		'wp-utils',
		'wp-i18n',
	), MY_PLUGIN_VERSION );
}

add_action( 'enqueue_block_assets', 'my_plugin_enqueue_editor_assets' );

The above then will be loaded as separate scripts and available through the global object (the window object since we’re in a browser environment) and we’d use them as with any externally loaded script in our code:

// With ES5 we'd call their methods directly:

wp.blocks.registerBlockType(/* ... */);

// With ESNext and object destructuring:

const { __ } = wp.i18n;
const { registerBlockType, InspectorControls } = wp.blocks;
const { Fragment } = wp.element;

// And then begin our block:

registerBlockType(/* ... */);

If you’ve been using ESNext (which I strongly suggest you do) with a build step that includes webpack, you could define these libraries as externals in your webpack configuration and use them as ES modules!

First we need to define our library and libraryTarget output in webpack’s config:

/... in your webpack.output object:

// ...
output: {
  // ... your paths here
  library: ['wp', '[name]'],
  libraryTarget: 'window',
},
// ...

This is required so that webpack knows exactly to which context to assign the global variables to. Then we define Gutenberg’s libraries as externals, again in webpack’s config on its externals key:

// ...
const webpackConfig = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'myplugin.build.js',
    library: ['wp', '[name]'],
    libraryTarget: 'window',
  },
  externals: {
    'wp.blocks': {
      window: ['wp', 'blocks'],
    },
    'wp.i18n': {
      window: ['wp', 'i18n'],
    },
    // And so on for all libraries
  },
};
// ...
webpack.config.js

Or we can be a bit smarter and avoid declaring every single one manually:

const wplib = [
  'blocks',
  'components',
  'date',
  'editor',
  'element',
  'i18n',
  'utils',
  'data',
];

const webpackConfig = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'myplugin.build.js',
    library: ['wp', '[name]'],
    libraryTarget: 'window',
  },
  externals: wplib.reduce((externals, lib) => {
    externals[`wp.${lib}`] = {
      window: ['wp', lib],
    };

    return externals;
  }, {}),
}

And that’s it, from now onwards we can “import” Gutenberg’s external libraries as ES modules:

import { __ } from 'wp.i18n';
import { Fragment } from 'wp.element';
import { registerBlockType, InspectorControls } from 'wp.blocks';

registerBlockType(/* ... */);

// etc...
myblock.js

Of course they won’t be included in our final build, this is mostly so that we keep a consistent usage syntax for our external dependencies. Another benefit is that if WordPress ever releases them as standalone npm packages our migration path will be much easier, especially if we name them @wordpress/data instead of wp.data (the name you give is your choice).

Leave a Reply

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