Joshua's Docs - Wordpress Gutenberg Block Notes

Gutenberg and the Block concept is such a radical change to WordPress, I felt it deserved a separate page, away from my main WordPress docs page (which you still should definitely check out 😀).

There is still a lot of overlap between a modern Gutenberg setup and the typical PHP plugin or theme development setup - so there might be things left out of this document because they are covered in the aforementioned main WordPress doc.


Resources List

Build Tooling


wp-scripts (full package name @wordpress/scripts) is a distributed package that contains scripts and tools that make WordPress development, building, and testing easier.

The domain of the wp-scripts is massive, and includes things like:

  • Transpiling, optimizing, and bundling JavaScript
  • Linting CSS, JS, and other files
  • Auto-formatting selected languages
  • Orchestrating webpack, and running a live-reload JS server
  • Supporting E2E tests, and orchestrating Puppeteer + Jest
  • And more!

It can be a little overwhelming at first, but again, a tool that you don't want to leave out of your WP arsenal.

You can find the entire source code of wp-scripts here

WP-Scripts: Extending Webpack

Create a webpack.config.js in the root. Import the default config, then extend it as you please, and re-export it:

// For better intellisense, I'm asserting that the imported object is a webpack config type
// @ts-ignore
const wpConfig = require('@wordpress/scripts/config/webpack.config');
/** @type {import('webpack').Configuration} */
const defaultConfig = wpConfig;

/** @type {import('webpack').Configuration} */
const finalConfig = {
	// Whatever else you want

// Don't forget to re-export!
module.exports = finalConfig;

Gutenberg Block Development - React and JavaScript Files

What's the Deal with All the IIFEs?

If you look through the source code of a lot of plugins that are out there, you will probably notice that a lot of them use IIFEs (Immediately-Invoked Function Expression) in various JavaScript files. Why?

The #1 reason is to provide closure; since WordPress's enqueue system doesn't use modules or anything that would guarantee closure, you have to make sure to not pollute the global JS space with your plugin's variables, functions, etc. IIFEs do this automatically, with or without a build system. However, if you use a standard build system, such as webpack, you shouldn't need to use IIFEs, since the bundler can automatically provide closure for you when it builds the final distributed files. In fact, using them if you can avoid them is probably a pattern to be avoided.

File Organization

A good approach to organizing blocks is to have each block isolated in its own folder, with each block stage (edit vs save) broken out into its own file. Each block folder can have a index.js that exports everything from the folder upwards, so in the parent folder that contains all your blocks, you can import them and register them together.

This StackOverflow response does a good job of summarizing.

TypeScript Support

  • Modify WebPack config to use ts-loader

    • I would turn on transpileOnly, for perf improvement, as well as avoiding checkJs conflict issue (it will try to check node_modules, regardless of exclude settings)

      • You can add a separate step as part of build or whatever that runs the type-checker specifically
    • You also need to tell WebPack to resolve .ts and .tsx filetypes.
  • Modify tsconfig:

    "allowSyntheticDefaultImports": true,
    "moduleResolution": "node",
    "jsx": "react-jsx",
    "module": "ESNext",
    "target": "ES5"
  • Modify loader scripts

    • ts-loader will cause the final files in /build to be main.js, main.asset.php, instead of index.js, index.asset.php

      • You could probably also fix this by monkey-patching the webpack config... For example, changing the entry to be map of names to entry points, instead of a single entry point, will emit different files

Gutenberg Block Development - Random Tips

  • Try out the wp-scripts package! It is clear WordPress is putting a lot of work into it, and I actually found it pretty enjoyable to work with, especially considering the alternative of rolling all my own config files, build tool setups, etc.

    • You still have to do some setup and tweaking, especially if you use TypeScript like I do, but it seems worth the effort.
  • Take advantage of Gutenberg's pre-build React UI components!

    • You can preview them all thanks to this hosted Storybook instance
    • These are great for things like the settings sidebar, admin panels, and inputs, as they are pre-styled to match the WordPress admin style.
    • There is usually documentation in each component sub-folder


Here are some guides on E2E testing with WP + Gutenberg Blocks:

Testing - Tips and Troubleshooting

  • Take advantage of e2e-test-utils!

  • How to disable the popup welcome-guide on the editor pages?

    • This approach worked for me! For using with Puppeteer, just put that JavaScript inside of a call to page.evaluate() instead of wp_add_inline_script.

WordPress Gutenberg Troubleshooting

WordPress Gutenberg - Troubleshooting Dynamic Rendering

  • Error loading block: Invalid parameter(s): attributes

    • Make sure that when you register the block in PHP, you pass in the associative array of attributes (even if you already passed them in when registering the block in JavaScript)
    • This is noted here
  • Error loading block: The response is not a valid JSON response.

    • Be careful about how you are returning the server-side generated HTML from your PHP function / method.

      • Although it will work (generally) for the front-end of the site if you simply echo out raw HTML (e.g. by stopping and starting PHP interpretation - ... ?> <h1>Hello</h1> <?php ...), this will break the API endpoint for previews, as it ends up with a non-serializable value.
      • The best-practice approach might be to manually build the string up, but a faster and easier approach might be to use output buffering to capture the generated HTML (like this)
      • WordPress takes the return value of your php render_callback method and tries to stringifiy / serialize it as part of a JSON object, which it returns to the editor UI via the API endpoint.
  • You are trying to inject JavaScript via tags (e.g. <script></script>), but the code is not getting executed within the edit preview page

    • This is because the ServerSideRender component simply uses React's dangerouslySetInnerHTML attribute, which does not eval script content, due to this limitation
    • This is a hard one to work around... you could use a library component like this one to auto-extract the JS portion and run it, or manually setup a little pattern to extract the JS and run it through eval().
Markdown Source Last Updated:
Fri Apr 16 2021 19:24:24 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Fri Apr 16 2021 14:18:57 GMT+0000 (Coordinated Universal Time)
© 2021 Joshua Tzucker, Built with Gatsby