Gutenberg Deep Dive


This is an in-depth lesson on the Gutenberg Block Development environment.

Good tools have been put into place to make block development magically “work” for WordPress developers. This is great, because it gets us up and running quickly with minimal headaches for the standard build process.

However, eventually, projects will eventually take us out of the “regular”, and then having some insight behind the scenes will go a long way.

If you’re only interested in mashing the “easy” button at this time, jump to the quick set up and skip the rest of the lesson for now.

The Heart of Gutenberg

Now that Gutenberg is bundled in WordPress Core, it’s files are bundled in the wp-includes/js/dist folder.

Within your php, these are often script dependencies with handles like ‘wp-a11y’

Within your JS Modules you can import these from the wp global.

import { speak } from wp.a11y

Or from an @wordpress packageThis witchcraft made possible with the @wordpress/dependency-extraction-webpack-plugin

import { speak } from '@wordpress/a11y';

AutoMagic environment setup

npx @wordpress/create-block wc-special-sauce-block
npm run start #initiates a watch task that creates a developer build of your block/plugin with each edit
npm run build #creates a development build of your block/plugin
npm run format:js #runs wp-wprettier npm package on your code
npm run lint:css #lints your css
npm run lint:js #lints your js
npm run packages-update #updates your npm @wordpress packages and dependencies

WordPress provides a plugin creator in the form of an npx package that spins up a plugin with a fully functioning build environment Note that magic isn’t free. This build environment uses ~415 MiB of disk space

Beyond the defaults

While the default npm scripts will work just fine with no CLI arguments, each script will accept specific arguments that you can use to customize the build process

if ( hasArgInCLI( '--webpack-no-externals' ) ) {
	process.env.WP_NO_EXTERNALS = true;

if ( hasArgInCLI( '--webpack-bundle-analyzer' ) ) {
	process.env.WP_BUNDLE_ANALYZER = true;

if ( hasArgInCLI( '--webpack--devtool' ) ) {
	process.env.WP_DEVTOOL = getArgFromCLI( '--webpack--devtool' );

const { status } = spawn(
	resolveBin( 'webpack' ),
	[ ...getWebpackArgs(), '--watch' ],
		stdio: 'inherit',
! isProduction && new LiveReloadPlugin( {
	port: process.env.WP_LIVE_RELOAD_PORT || 35729,
} ),
// WP_NO_EXTERNALS global variable controls whether scripts' assets get
// generated, and the default externals set.
! process.env.WP_NO_EXTERNALS &&
	new DependencyExtractionWebpackPlugin( { injectPolyfill: true } ),

To conduct a detailed analysis of what’s possible, take a look file specific to the script you want to customize in the @wordpress/scripts/scripts directory

A PHP build?

For most of us that have spent our most of our time developing open source WordPress plugins and themes, the notion of the PHP file being automatically built is a bit weird.But a bit more common, for those that have worked with the PHP Composer

But that’s exactly what happens in the npx create-block’s start and build processes. A *.assets.php file is spit out for each of our block entry points–giving us automatically generated values for registering our scripts.

<?php return array(
	'dependencies' => array( 'wp-blocks', 'wp-element', 'wp-i18n', 'wp-polyfill' ),
	'version'      => 'a125b1d2396f9fe3926b882158dc7cd0',

Note the weird version number, which is a webpack runtime chunk.

WooCommerce Product blocks

"scripts": {
	"build": "rimraf build/* && cross-env BABEL_ENV=default NODE_ENV=production webpack",
	"build:deploy": "cross-env WOOCOMMERCE_BLOCKS_PHASE=2 composer install --no-dev && WOOCOMMERCE_BLOCKS_PHASE=2 npm run build --loglevel error",
	"build:e2e-test": "npm run build",
	"build:map": "cross-env BABEL_ENV=default NODE_ENV=production FORCE_MAP=true webpack",
	"changelog": "node ./bin/changelog",
	"changelog:zenhub": "node ./bin/changelog --changelogSrcType='ZENHUB_RELEASE'",
	"deploy": "npm run build:deploy && sh ./bin/",
	"dev": "rimraf build/* && cross-env BABEL_ENV=default webpack",
	"explore": "source-map-explorer",
	"lint": "npm run lint:php && npm run lint:css && npm run lint:js",
	"lint:ci": "npm run lint:js && npm run lint:css",
	"lint:css": "stylelint 'assets/**/*.scss'",
	"lint:css-fix": "stylelint 'assets/**/*.scss' --fix",
	"lint:js": "wp-scripts lint-js",
	"lint:js-fix": "eslint assets/js --ext=js,jsx --fix",
	"lint:php": "composer run-script phpcs ./src",
	"package-plugin": "./bin/",
	"package-plugin:dev": "./bin/ -d",
	"package-plugin:zip-only": "./bin/ -z",
	"package-plugin:deploy": "npm run build:deploy && npm run package-plugin:zip-only",
	"phpunit": "docker-compose up -d db && docker-compose up -d --build wordpress-unit-tests && docker exec -it --workdir /var/www/html/wp-content/plugins/woocommerce-gutenberg-products-block wordpress_test php ./vendor/bin/phpunit",
	"reformat-files": "prettier --ignore-path .eslintignore --write \"**/*.{js,jsx,json,ts,tsx}\"",
	"release": "sh ./bin/",
	"start": "rimraf build/* && cross-env BABEL_ENV=default webpack --watch --info-verbosity none",
	"storybook": "start-storybook  -c ./storybook -p 6006 --ci",
	"storybook:build": "build-storybook  -c ./storybook -o ./storybook/dist",
	"test": "wp-scripts test-unit-js --config tests/js/jest.config.json",
	"test:e2e": "npm run wp-env:config && cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js",
	"test:e2e-dev": "npm run wp-env:config && cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --puppeteer-interactive",
	"test:e2e:update": "npm run wp-env:config && cross-env NODE_CONFIG_DIR=tests/e2e/config wp-scripts test-e2e --config tests/e2e/config/jest.config.js --updateSnapshot",
	"test:help": "wp-scripts test-unit-js --help",
	"test:update": "wp-scripts test-unit-js --updateSnapshot --config tests/js/jest.config.json",
	"test:watch": "npm run test -- --watch",
	"wp-env": "wp-env",
	"wp-env:config": "./bin/"

If your ambition is to work within the Product Blocks structure for the eventual inclusion of your code into WC core, you’ll have a significantly more build structure to contend with. We’ll discuss this briefly in the custom blocks lesson.


npx @wordpress/create-block can get you going pretty quick.

Just keep in mind, the React build processAnd, therefore, Gutenberg involve a lot of hidden complexity. When that complexity starts to trip you up, take a peak behind the curtain and adjust your configuration accordingly.

Up next we’ll look at the existing block ecosystem for WooCommerce, so we don’t have to reinvent the wheel. But first, let’s see what you learned 🙂.

Knowledge Check



Keyboard shortcuts

CTRL+Shift+F Search slideshow
F Fullscreen view
CTRL+Click Zoom in
Esc Topic overview
Right arrow,
Down arrow
Next slide
Left arrow,
Up arrow
Previous slide

Color codes

Hover over text more additional info
Link to an external resource
Link to an internal slide
If buttons aren't working, click in the screen to "focus" your browser