, ,

Hooking into the Block Editor’s Post Publish Panel (with Copilot)

First, a quick recap: I’ve been working on a WordPress plugin called “Habit Streak” with Github Copilot. I’m trying to see how far Copilot can take me without resorting to writing the majority of the code myself. You can read the previous article for more of a background on the entire project, but the main gist is that it’ll be a plugin that shows an author their current blogging “streak”.

So far, there’s been a few instances (especially around finding a performant way to calculate “streaks”) where I’ve just powered through the code myself or really had to tell Copilot what I wanted, but overall I really am trying to rely on the pair programming method. As of this point, the plugin shows our blogging streak on the WordPress admin bar:

WP Admin Bar

I have a few settings that I want to add (selecting a specific post type, ignoring certain days when counting a streak) and some performance/storage work, but we’ll get to that later. For today’s sprint, I want to set the foundation for eventually adding our streak information to the block editor panel that pops up after you click publish. This panel right here:

The post-publish sidebar/panel.

In the old days, injecting content into the post edit screen was the matter of a few simple PHP hooks. I haven’t messed with the modifying the block editor itself, so this should be fun. Let’s get started.

Build Process

To do anything with block editor, I typically start by installing the @wordpress/scripts package. It’s essentially a fancy webpack tool that lets you move anything from a src folder into build folder. It’s pretty smart at recognizing everything from dependency packages in WordPress to custom block scripts.

Once I’d installed it via NPM, I created a src/index.js file and wrote a single line comment at the top:

// Hook into the PluginPostPublishPanel componentCode language: JSON / JSON with Comments (json)

The PluginPostPublishPanel component is what we’re looking for (I had to do some digging on that one). After that I just accepted literally everything Github Copilot recommended, then picked it apart to see what it would do. Let’s walk through it all.


First, Copilot gives us some functions from various WordPress packages:

const { PluginPostPublishPanel } = wp.editPost;
const { registerPlugin } = wp.plugins;
const { withSelect } = wp.data;
const { compose } = wp.compose;
const { __ } = wp.i18n;Code language: JavaScript (javascript)

I can see I’m grabbing the PluginPostPublishPanel component that I need to output stuff with. I’m also grabbing the registerPlugin function, which is our way of telling the block editor that we have some code we want it to run. What’s interesting is that it includes a few other functions, like withSelect, something that lets you interact with WordPress post data. Copilot has something in mind, but we won’t know what it is just yet.

However, there’s already some differences in how these are being pulled, as compared to the documentation. Here’s the docs using the import method instead:

import { registerPlugin } from '@wordpress/plugins';
import { PluginPostPublishPanel } from '@wordpress/edit-post';Code language: JavaScript (javascript)

Notice how we’re importing from packages rather than declaring variables from the global wp object. From what I understand, WordPress really prefers you do the newer second method. So instead, I add a new comment (// Import the dependencies ) and Copilot actually rewrites those lines as imports:

// Import the dependencies
import { PluginPostPublishPanel } from '@wordpress/edit-post';
import { registerPlugin } from '@wordpress/plugins';
import { withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';Code language: JavaScript (javascript)

Writing our plugin code

Next, I let Copilot write the entire function for me. What I mean is, I literally just keep accepting lines of code until I think it’s done. Here’s what happens:

const MyPostPublishPanel = compose(
	withSelect( select => {
		return {
			postType: select( 'core/editor' ).getCurrentPostType(),
)( ( { postType } ) => {
	if ( 'post' !== postType ) {
		return null;
	return (
			<p>My Post Publish Panel</p>
);Code language: JavaScript (javascript)

So, a few things are happening here. It looks like Copilot has decided to check and confirm that I’m on the post post type. I hadn’t thought that far in advance, but actually I will want to be able to filter this by post type in the future, so that’s handy. That also explains why it wanted the withSelect function available.

Then it returns the PluginPostPublishPanel component with some custom content, which I’ll return to later. Unfortunately, I had some issues getting this to work. Eventually I added one more blank line at the bottom of my file and Copilot made one more recommendation:

registerPlugin( 'my-post-publish-panel', { render: MyPostPublishPanel } );Code language: JavaScript (javascript)

Of course! I had to actually register my plugin with the block editor. Other than that, I won’t need to edit this file at all, until I’m ready to load my habit streak number (and probably rename my plugin). It just works.

The last step is running our build script which came directly from @wordpress/scripts and looks like this: npm run build. Our finished files show up in the /build directory.

Enqueueing our Script

Jumping back into PHP, it’s time to enqueue our JavaScript file so that it loads in the block editor. This time I start writing a function in my plugin’s class called enqueue_block_editor_assets(). Here’s what Copilot generates for me:

public function enqueue_block_editor_assets() {
		plugins_url( 'build/index.js', __FILE__ ),
		array( 'wp-blocks', 'wp-element', 'wp-editor' ),
		filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' )
}Code language: PHP (php)

It knows the name of our plugin and does a pretty good job of grabbing the build/index.js file, giving it a version number, and naming a few decent dependencies. Here’s the thing, though: those aren’t the right dependencies.

When you run your build process, @wordpress/scripts also creates a build/index.asset.php file. It’s essentially a line of PHP that returns an array with two keys in it: dependencies and version. Here’s what mine looks like:

<?php return array('dependencies' => array('wp-compose', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins'), 'version' => '5766a0545ce407b044c9');Code language: HTML, XML (xml)

Those are actually the dependencies I need (and the randomized version string doesn’t hurt either). I write a comment to Github above my enqueue script: // Get dependencies from index.asset.php.And with very little finessing, here’s what the result is:

public function enqueue_block_editor_assets() {

	// Get dependencies from index.asset.php.
	$asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php' );

	// Enqueue the bundled block JS file.
		plugins_url( 'build/index.js', __FILE__ ),
}Code language: PHP (php)

I add this to the enqueue_block_editor_assets action in my constructor and boom- everything is working! I publish a post and actually see my little panel showing up under the URL.

Final thoughts

So first of all, I should mention that I didn’t write this article in the order that I actually did everything. There was a ton of back-and-forth. I enqueued everything early on and dealt with a ton of console errors and failed code. It was definitely a trial-and-error approach, but this post would’ve been around 5000 words if I highlighted every issue I had.

That said, it was very similar to what it would’ve been like if I were to have done this myself. I would’ve been trying out code from the documentation, futzing around in Stack Overflow, or more likely bugging my viewSource cohost in Slack.

I’m fairly new to modifying the block editor, and I’m not sure yet if I like using Copilot for such unfamiliar territory. I’m spending a lot of time dissecting small pieces of Copilot’s code and comparing it to the documentation, which is just… different. Copilot saves time from typing, but it can also give you pretty mediocre or outdated code.

There’s a few more steps to actually displaying the habit streak value in our panel, which I may document in a future post. I could potentially use wp_localize_script() to just pass the value from PHP to our file. That however, feels like the old/bad way to do it. Not to mention, the very act of publishing our post will update our streak (probably on the save_post hook) and require us to fetch the latest data. Fetching data from the @wordpress/data package is… well… not simple, so I have a good feeling that’ll be our next post. If you have any good resources on the topic, please share them in the comments or on the socials somewhere.

👉 Browse the full source code of the plugin at the time of this post.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.