Principal Software Engineer at WP Engine in Denver, CO. I love problem solving and working with WordPress. I'm the creator and maintainer of WPGraphQL. I'm passionate about Open Source software and the web. When I'm not writing code, you'll find me spending time with my beautiful wife Rachel and my 2 sons Andrew and Blake. I also enjoy playing and watching soccer.
A friend of mine posted the following statement on Facebook.
I would agree that fatherlessness is indeed problematic.
And Black families are more likely to be lead by a single parent than white, Asian, or Hispanic/Latino families.
But what is the root of fatherlessness, specifically in Black families?
Let’s explore.
While Black men sometimes do willingly walk away from their families, I’d argue that the problem at large is less of a result of Black men willingly abandoning their families, but more of a result of systemic racism in action.
Hear me out.
According to pewresearch.org* 98% of criminal charges never see a trial.
This means that if you are arrested and charged with a crime, small or large, there’s a very high chance you won’t have a chance to walk away without serving some time. You’re faced with the choice of accepting a plea deal with a lesser sentence or going to trial and serving the max sentence.
Since more than 90% of all trials end up with a guilty verdict**, folks that have been arrested and charged almost always take the plea deal because they know that if they choose to go to trial, they’re almost guaranteed (greater than 90% chance) to serve the max sentence, whether they committed the crime or not.
While white males have a 1 in 17 chance of being arrested in their life, black males have a 1 in 3 chance of being arrested.
The rate of black arrests is 5x that of white arrests.
So if black males are arrested at a much higher rate, and anyone charged has a 97% chance of never going to trial or 90% chance of a guilty conviction of a trial happens, you do the math.
Our prisons our filled with black men.
Our Black families are left fatherless.
To further compound the problem, once you’ve served time in prison, there’s an 83% chance*** you will return to prison.
The system is very heavily in favor of keeping Black men in our prisons, away from their families.
Shortly after Kobe Bryant died I was in Los Angeles with my family for the February 2020 Gatsby company gathering.
My family was at a restaurant and my son points at a man at the bar and says “That man looks like Kobe Bryant”.
I look to the bar and the only man I see is a 40-something white man with facial hair. I ask my son to clarify. “Which man looks like Kobe Bryant?”. And he points again at the same man. I pull up my phone and show him a photo of Kobe Bryant to make sure he knows what Kobe Bryant looks like. He confirms and says, “ya, he looks like Kobe because they’re both tall”.
On the same trip, my kids met my colleague Hashim Warren, a Black man. Hashim, wearing a blue shirt, offered to watch our kids one of the evenings so that Rachel and I could participate in some of the team activities. In the car ride back to the hotel, my kids asked when “the blue guy” was going to watch them. To this day, they still refer to Hashim as “the blue guy”. To them, his Blue shirt was what made him unique, not his skin color.
My children don’t see people around them as different because of skin color. To my children, a person’s height and color of their shirt is what stands out as a differentiator.
To me, these were proud parenting moments.
The reality of being “colorblind”
I was raised to be “colorblind” and not treat people differently because of skin color, and I’ve been proud to be raising my children this way as well.
When I was a child, some of my best friends were Black, Vietnamese, Hispanic and Iraqi. I don’t treat anyone different because of skin color.
It feels and sounds “right” to treat everyone equal regardless of skin color, right?
When racial issues arise in the media, I’ve largely ignored them because I think it’s “other” people that need to change. I think to myself, “I don’t treat people poorly because of their skin color, I’m not part of the problem“.
After reading many stories of folks that have converted from shouting “All Lives Matter” to becoming vocal “Black Lives Matter” supporters, it’s been clicking for me.
For 33 years, I’ve been “colorblind” and treated everyone as equal, as if that’s the “right” thing to do.
I was wrong.
The sad reality is that we’re not equal.
I’ve never been followed in a department store. I’ve never been handcuffed after being pulled over. I’ve never been arrested for robbing a bank just because I have the same skin color as the person that did commit the crime.
The dream, of course, is that one day we will actually be equal.
Growing up, I ignorantly thought that day had already come. It hasn’t, and by treating everyone as equal, I’m part of the problem. There’s a large equality gap that needs to be filled.
The Lightbulb Moment
This image was one of the things that’s helped make it finally click for me.
My life isn’t the house on fire. I fortunately face very little adversity. I have very little fear or risk when interacting with police officers.
I’m the epitome of White Privilege.
Being “colorblind” and going about my day thinking “everyone is equal” is blissful ignorance.
Black people all around me are the house on fire. They’re the ones at risk. They’re the ones living in fear, being followed in department stores, being handcuffed when pulled over, or worse. And I’m over here with my fire hose of white privilege and I’ve not been using it to help put out that fire.
For 33 years I’ve thought that “not treating people worse based on skin color” is “good enough”.
It’s not good enough.
Taking Action
I don’t know what all I can/should do to make a meaningful difference, but I know I can no longer be a passive bystander.
I have a lot more to learn, so I’m starting by picking up a copy of “How to be Antiracist”, as recommended by Madalyn Parker (and many others).
I want to take action. Now.
I have a bit of a following with the WPGraphQL brand that I’ve built over the past few years. I’ve used that to make make an explicit statement that “Black Lives Matter”. It’s not much, but it’s something.
Something that’s bothered me about US culture for a long time, is that our justice system targets minorities and is setup to make it nearly impossible to escape the system. The US makes it nearly impossible to re-integrate into society after being released from prison. It’s difficult to learn new skills in prison, and many companies won’t hire if you have a criminal record. If you can’t get a job after prison, how can you stay out of prison?
In addition to being nearly impossible to escape the system, the chances of you getting into the system in the first place are much higher if you’re Black. According to the NAACP, “African Americans are incarcerated at more than 5 times the rate of whites”.
I largely feel helpless in addressing these issues, but doing nothing doesn’t feel like an option anymore.
I’ve applied to be a volunteer instructor for The Last Mile, an organization that teaches Web Development to prisoners. I want to do something to actively make a difference and change the narrative in our society.
I’ll be looking for more opportunities to actively make a difference.
Last night I had the pleasure of presenting at the Denver WordPress Developer meetup about building Gutenberg Blocks. In this post, I want to present two (of many) approaches to building Gutenberg blocks.
First, some context
Gutenberg is the new block-based editor coming to WordPress 5.0, currently scheduled for release on November 27, 2018.
This new editor allows users to compose content using “Blocks”. Blocks can be anything, from paragraphs of text, to images or galleries, to embedded videos, contact forms, dynamic content from post types or external sources. . .the possibilities are endless.
Blocks have some conceptual similarities to shortcodes, in that blocks allow you inject rich elements into the content created with WordPress, but unlike shortcodes, Blocks provide a rich UI to interact with and configure. This rich UI is a JavaScript application, built on top of React and Redux, that is enqueued into the WordPress admin.
Two approaches to building blocks
Getting started building blocks with Gutenberg can be a struggle. At the moment, official documentation for Gutenberg is still very incomplete and even much of the existing documentation is outdated and incorrect. So it can be tough to figure out how to build blocks and what options there are to make the process of building blocks easier.
In order to compare experiences, we’ll look at what it takes to make the same block using both Advanced Custom Fields and Create Guten Block.
Build a testimonial block
The block we’re going to create is a simple “Testimonial” block, that allows for a testimonial text to be added, an avatar image, a citation name, some color options for setting the background color and text color, and some alignment options.
Below is an GIF showing the final block in action. While the preview is of the block built using ACF, the goal is to build a comparable block using Create Guten Block.
Building the block with Advanced Custom Fields
The first approach I want to look at is using Advanced Custom Fields. In my opinion, this is the quickest way to get up and running with building custom blocks for Gutenberg. It requires the least amount of code, in this case only PHP code and no custom JavaScript and provides some very nice features with minimal effort.
Install Advanced Custom Fields
In order to build custom Gutenberg Blocks with Advanced Custom Fields, you will need version 5.8 or newer. At the time of writing this, v5.8 has not been released to the public, but is available to ACF Pro customers and can be downloaded from the ACF dashboard. The Gutenberg features are planned to be released to both the FREE and PRO versions of the ACF plugin.
Once the plugin is installed, we can start building our custom block.
Step 1: Register the Block
The first thing you’ll want to do is create a new plugin. To do that, create a new directory in your WordPress /wp-content/plugins/wp-content directorynamed gutenberg-testimonial-acf-example.
In that directory, create a file named gutenberg-testimonial-acf-example.php and add the following code:
<?php
/**
* Plugin Name: Gutenberg Testimonial ACF Example
*/
Now we have a plugin that we can activate in our WordPress dashboard.
Next, add the following code to register your block with Advanced Custom Fields.
/**
* Register the block once ACF has initialized
*/
add_action( 'acf/init', 'my_acf_init' );
function my_acf_init() {
// check function exists
if ( function_exists( 'acf_register_block' ) ) {
acf_register_block( [
'name' => 'acf-testimonial',
'title' => __( 'Testimonial - ACF', 'gutenberg-testimonial-acf-example' ),
'description' => __( 'A custom testimonial block, using Advanced Custom Fields Pro.', 'gutenberg-testimonial-acf-example' ),
'render_callback' => 'acf_testimonial_callback',
'category' => 'formatting',
'icon' => 'admin-comments',
'keywords' => [ 'testimonial', 'quote', 'acf' ],
] );
}
}
/**
* Render Callback for the block. This is what is output in the Theme AND
* in the preview within Gutenberg
*
* @param $block
*/
function acf_testimonial_callback( $block ) {
?>
<h2><?php echo $block['name']; ?></h2>
<?php
}
This code is registering a Gutenberg block using the acf_register_block() function. By registering our block with this function, we have a new Gutenberg Block that we can use in Gutenberg, and we have a new Block “location” that can be used to assign Field Groups to in ACF. We’ll take a look at that in a second.
You’ll notice that along with registering our block, we’ve also created a callback function that is called by our blocks render_callback.
This callback works much like a shortcode callback, in that the Block attributes will be passed to this callback, and the callback will return the output for the block. In this case, we’re outputting an <h2> tag with the name of the Block.
Gutenberg core uses the render_callback to render blocks in the theme, but you’re still responsible for creating a JavaScript implementation of your block to preview in the Editor. ACF makes use of the render_callback to preview your block in the Editor as well, allowing you to build custom blocks without writing any custom JavaScript.
Here’s a preview of the block at this stage:
We can see the block can be added to the content of our post, and we see the title of the block in an <h2> tag both in our Editor and in our theme. . .but we can’t really do much with the block yet.
Step 2: Write our markup and styles
In this next step, we’ll write the markup that we’ll use to output our block, and write some styles to go with it.
Here the first thing we do is define some variables that will eventually be dynamic values from our ACF Fields. Then we write some markup and a <style> tag to go with the markup, and we use our variables in the appropriate places to make our block dynamic.
Note: you could/should move the static styles to an external stylesheet and enqueue, then use the <style> tag only for the dynamic styles. For brevity, I’ve included all styles, static and dynamic in the style tag.
At this point, our block is starting to take shape. We can add the block to our editor, and you can see that it has some structure and style to it and is consistent in the editor and our theme. . .but it’s still not editable.
Step 3: Register fields and make the block editable
Now that we have a block registered using acf_register_block() our block is now considered a valid location for adding Field groups to using ACF.
Using the ACF UI, we can create a new field group and add the fields we’ll need and apply the field group to our block.
For this block, we know the dynamic values we want to edit, so we can create the following fields:
Testimonial: Textarea field for editing the testimonial text
Name: Text field for who citation of who said the testimonial
Avatar: Image field for uploading an image to the Testimonial
Alignment: Select field for choosing Right or Left alignment
Background Color: Color Picker field for selecting the background color
Text Color: Color Picker field for selecting the text color
Then, we can add our Field Group to our block, as it’s a valid Field Group Location because of acf_register_block(). See below:
You can also register your ACF Fields using PHP or JSON and keep your Fields versioned. In our case, we’ll use PHP.
Add the following code to your plugin, right below where we registered our block.
This is registers the fields we need so we don’t need to use the ACF UI, and the location is set to our block.
At this point, ACF has done the heavy lifting for us by wiring up our fields to our block. So now we can interact with our block and edit the fields we’ve defined. But we need to wire up the values of the fields to update our block in the render callback.
We’ve already defined the variables, so now we just need to swap them with the field values.
In your render callback, replace the variable definitions with the following:
Here, instead of hard-coding our values, we use ACF to get the field values, using the ACF function get_field(). ACF does some magic to know the context of the block, so all we have to specify is the name of the field we want to get, and we’re good to go. The get_field() function is unique to the specific block, so you can have many blocks on one page each with their own dynamic values and get_field() will get the values for that specific block.
Now, with our fields registered and wired up, we have a fully interactive custom block, and haven’t written a single line of JavaScript.
Building the same block with “Create Guten Block”
Create Guten Block is a fantastic project from an incredibly influential member of the WordPress – and greater Open Source – community Ahmad Awais.
In the same vein as Create React App, Create Guten Block is a tool that scaffolds out a project with pre-configured webpack setup optimized to reduce the friction needed to build Gutenberg blocks using React and ES6. You can read more about the project on Github: https://github.com/ahmadawais/create-guten-block.
Step 1: Scaffold the block
Note: You’ll need the latest version of node/npm installed on your machine to work with Create Guten Block.
The first thing you’ll want to do is create a new plugin. To do that, create a new directory in your WordPress /wp-content/plugins/wp-content directorynamed gutenberg-testimonial-cgb-example.
In that directory, create a file named gutenberg-testimonial-cgb-example.php and add the following code:
Now, our plugin with a sample block is wired up and ready for us to start hacking on. From the command line, at the plugin root, run the following command to have Webpack start watching for changes: npm run start
Step 2.1: Understand what was scaffolded
At this point, we’ve got a plugin ready to go with an actual functional block. You can add it to a Gutenberg post right now. So, how does this work?
/src/init.php
At the root we are requiring the /src/init.php file, so let’s take a look at that.
In this file, we’ve got 2 hooks executing 2 callback functions.
The first hook is enqueue_block_assets and that executes the callback testimonial_cgb_cgb_block_assets().
This new hook enqueue_block_assets() is run anywhere Gutenberg is enabled, whether that’s the admin or the front-end of a Gutenberg-enabled post/page.
In this case, we’re enqueueing just one stylesheet to be loaded in both the theme and the editor. This will allow us to share styles across the front and back end.
You’ll note that we’re enqueuing a file /dist/blocks.style.build.css. This file is generated by the Create Guten Block webpack build whenever we run npm run build. It takes our .scss files and minifies them to a single CSS file that we can enqueue to WordPress.
The next hook is enqueue_block_editor_assets. This executes the callback testimonial_cgb_cgb_editor_assets(), and that callback enqueues a JavaScript file and another CSS file.
The enqueue_block_editor_assets hook is only executed in the context of the Gutenberg editor, so these are assets we need for the editing experience of Gutenberg, in our case any custom CSS we’ll need to style the editor and the JavaScript needed to interact with our block in the editor.
Both files we’re enqueueing are “built” files generated by running npm run build. These are not the files we will use to edit directly.
/src/blocks.js
This is the entry point for the WebPack script to build our final distributable JavaScript from. So any JavaScript file we import here will be included in the final build. In our case, we’re importing just a single js file from /src/block/block.js. That is the block file we’ll be working with. You can build multiple blocks and import them to this entry point just like we’re importing this one block.
/src/block/block.js
This is the main file we’ll be interacting with. Let’s move to the next step and you’ll get more familiar with this file as we go.
Step 3: Clean up scaffolded code
Create Guten Block scaffolds out the code with a lot of inline comments, which are great for context, but I want us to start in a clean spot with just the code, so let’s replace the /src/blocks/block.js file with the following:
Here, we’re saying we want to make use of the __ function and the registerBlockType function from wp.i18n and wp.blocks libraries respectively.
If you look back in our init.php file, you will note that when we enqueued our Block editor JavaScript file, we declared some dependencies: 'wp-blocks', 'wp-i18n', 'wp-element'.
These are libraries provided by Gutenberg (soon to be WordPress core) that expose functions for us to make use of in our custom Gutenberg code. We need to declare that we want to use these functions and where they’re coming from before we make use of them, so that when webpack builds our final JS file, it builds in the proper order ensuring dependencies of one file are included before they’re being used.
Register the block
Next we use registerBlockType to register the block. This function tells the Gutenberg application that a new block is available for use in the Gutenberg editor.
The key things to note here are the editandsave parts. When building blocks in JavaScript, we have an interface to edit the blocks, then we have to translate that into markup to be saved to the post’s content. What we save is what will be rendered by the theme, so we only want to save markup we plan to use in the output of the theme. So there tends to be some overlap between the Edit and Save methods, but the Edit method should provide extra functionality for interacting with the block in the editor.
In our case, currently, for save and edit we simply return an <h2> tag.
At this point, our block looks like so:
Step 4: Initial Markup and Styles
Let’s write our initial markup and styles so we can start seeing our Block come together.
Let’s create a BlockContent component that will return our HTML structure (the same HTML we’re using in the ACF example).
This is a functional Component that simply returns our Markup.
Above the return statement, we declare some dynamic values testimonial, avatarUrl and name. These eventually will be fields that we will edit, but for now we’ve hard-coded their values. Then we output them in our markup using squiggly brackets, such as {testimonial}.
Next, let’s create an EditBlock component. For now, it will just return the <BlockContent />, but we’ll build on it shortly to provide interaction.
Also, make sure to add const { Component } = wp.element; at the top of the file, as we’re now extending the Component from the core WordPress element scripts.
Now, update the edit part of our registerBlockType to be: edit: EditBlock,and update thesave to be:
So, right now, our Edit and Save functions are both returning the exact same <BlockContent /> component, but we’re set up to start changing the editing and saving experience.
This is a pretty big step, so instead of showing the code changes here, check out the DIFF on Github.
I’ll explain a few parts here though.
Declare attributes
If there are any dynamic properties of a block we want to edit, we need to declare them. Gutenberg calls these dynamic properties “attributes”.
In our case, we know we need the following dynamic properties to be editable: testimonial, avatarUrl, avatarId, name, background_color, text_color, and alignment.
Now, we want to add Inspector Controls to our block. In Gutenberg, when you select a block, on the right is a panel that allows you to modify block settings. This is referred to as the “Block Inspector”.
We’re going to make use of existing Gutenberg-provided components to build out our Inspector controls.
There’s a lot going on here, so I’ll explain a bit.
First we define the backgroundColors we want to be available in our color pickers, and we define the alignOptions we want to be available to our alignment Select option.
Next, we pluck setAttributes and our actual attributes out of this.props for use in the component.
In React, data passed down from one component to another are called ‘props’. So, in order for the Inspector to have access to setAttributes and the attributes, these will have to get passed to the <Inspector> component, and we’ll see where that happens later.
Next, we return an instance of <InspectorControls> with our nested controls.
I’d argue that it’s fairly easy to understand what’s happening here, even if you’re not a React expert, as it kind of reads like HTML now. We can see that inside our inspector we have a PanelBody, and inside that we have 2 instances of PanelColorSettings (color pickers for the background and text color) and a SelectControl for selecting the alignment option.
Each of these controls has an onChange method, which passes the value through and calls setAttributes() where the new value of the field is saved by Gutenberg to the block.
Now, in our EditBlockContent component, we’ve also made some changes. I’ll let you digest some of them on your own, but want to point out one specific change.
Our return statement returned an array previously. It still returns an array, but now the first thing it returns is <Inspector { ...{ setAttributes, ...this.props } } />,
This is the Inspector component we just built which includes all of our form controls for editing the block, and the next thing it returns is our actual editable block.
So, we’re telling Gutenberg that we have multiple components at work here. We have the Inspector and the Block itself. So now, when users select our block to edit it, Gutenberg does the heavy lifting of showing the user our Inspector controls and our block in a selected/editable state. And here, we’re passing the props from the Block instance down to the Inspector, which is how the Inspector has the ability to access the attributes and call setAttributes() on behalf of the block.
At this point, we have a block that has Inspector controls that you can interact with, but our block still doesn’t respond to changes in the controls. We’ll wire that up next.
Step 6: Connect the block to our Inspector Controls
Now that we have a block and inspector controls, we need to wire it up to respect the changes of the inspector controls and react (pun intended) to the changes.
We also are missing the ability to edit the testimonial text, citation text, and set the avatar image. We’ve opted to make those inline fields instead of Inspector fields (just to show another approach).
The DIFF for this step is a lot to grok again, so take a look at the DIFF here and digest what you can.
One thing to note, is that we’ve added a utility called classnames. You can add that yourself by running npm install classnames --save or by manually editing your package.json as seen in the Diff.
We’ve also added an icons.js file which exports an SVG Icon we’re using in this step.
So now, back in the block.js file we import classnames from 'classnames'; and import icons from './icons';. Note the first import isn’t relative, so it will come from our node_modules, but the second import is relative to our project.
Next, if you look at our EditBlock component, it’s changed quite a bit.
It’s replaced some of our otherwise “normal” HTML markup with some editable components. For example, our Testimonial is now a <RichText> component that is configured to change the testimonial attribute when it’s changed. This allows us to type inline in the block and have the testimonial change. We can also declare what kind of RichText features to support, such as bold, italic, strikethrough, etc.
We use the RichText component again for the citation, and we use a <MediaUpload> component for selecting the avatar image. The MediaUpload component is set to modify the avatarUrl and avatarId attributes when we select an image from the media library.
Now, if we look at the save method, it’s changed quite a bit as well.
At the top, we get the attributes we need from the props that the block passes down:
Then we tell Gutenberg how we want the content to save. We were previously saving raw HTML with a few dynamic values, but that will create some funky formatting when we get the values from the RichText component, so instead we need to use <RichText.Content> to save our content.
Then we also make use of some dynamic style={{}} tags to apply the values of the color pickers to our dom nodes so the dynamic styles can be saved and rendered in our theme.
At this point, our block is fully functional:
Conclusion
In my opinion, the developer experience provided by ACF is closer to what I would have expected the core Gutenberg developer experience to be. I believe that Gutenberg core should provide a lot of UI elements that can be used by sending up a description of blocks from a server side registry, Gutenberg reads that description of blocks, and renders the UI to interact with them. ACF provides this API. We didn’t have to write a single line of JavaScript and we had a fully functioning Gutenberg block.
The ACF integration is still young and there are limits to it, but it seems like it’s evolving very nicely, and I hope the Gutenberg core team pays attention and works toward an API that allows blocks to be created in a similar fashion where a server side API can be used to build blocks, and custom JS can be added only when the PHP API can’t get you the custom results you need.
I’ve written quite a lot on why Gutenberg needs a server-side API here and here already, but we can certainly add improved Developer Experience to that list as well.
The Create Guten Block experience is not bad. . .at least speaking from the perspective of someone who’s done quite a bit of React work already and genuinely enjoys React development. . .If you’ve never used React or aren’t that comfortable with JS, I can’t imagine how frustrating it would be to navigate the ecosystem right now.
Even with a background in building React apps, building Gutenberg blocks does require a LOT of knowledge of JavaScript, React, certain historical decisions of the Gutenberg project, lightly/undocumented features of Gutenberg, es6 (you can write blocks in es5, but I wouldn’t recommend it), etc. There are certain ways in which Gutenberg deviates from standard React, so you have to learn and get used to some new conventions.
We had to write a whole lot more code to get to essentially the same outcome as our ACF block, and in this case, the server has NO knowledge our block even exists, which I would argue is a bad direction for WordPress to head in.
With the ACF approach, the server is fully aware of the block, so tools like WP-CLI, REST API, WPGraphQL, etc can expose data about the registered block and the block becomes immediately more useful than our client-only block.
I do believe that building blocks on the client does empower developers to create more unique experiences so I don’t think I’ll ever argue that Gutenberg should not have a rich JavaScript API for building/extending blocks, but I definitely will continue to argue that the primary API for blocks should be a Server Side API, similar to what ACF (or Gutenberg Fields Middleware) are doing, where users can register blocks on the server, and that block registry is used to build the client representation without any JS needed. Then custom JS should be brought in for the situations where a generic API could not fulfill the blocks needs.
Anyway, I hope this guide helps show some possibilities of Gutenberg and helps ya’ll understand some options for working with it.
The Gutenberg editor (slated for WordPress core release in v5.0) ships with more than 60 blocks, many of which are likely irrelevant for many projects.
The official Gutenberg documentation provides methods for whitelisting and blacklisting blocks, but at the time of writing this, those documented methods don’t work, and there are several open issues in the Gutenberg Github repo regarding this.
If you need to whitelist (or blacklist) blocks today, below is an outline of how I accomplished this.
Note, that at the time of writing this, I’m using Gutenberg v2.8, and based on how quickly Gutenberg is moving, I don’t have much confidence that this will work for future versions, so take my tips/tricks with a grain of Gutenberg salt.
tl;dr
The approach below documents how to whitelist blocks in Gutenberg. You can use this to blacklist if that’s your cup of tea as well. The gist of what needs to happen:
Defined a list of allowed blocks
Pass that list from the server to the client on page load
Use that list with some custom JS to remove blocks that are not whitelisted
Define the list of allowed blocks & pass to the client
Gutenberg core ships with an allowed_block_types filter, but there are several issues open related to this filter not being well respected by the Gutenberg client (#6363, #6070, #5893), so before you get burned trying to use this, we’re going to define our own list of blocks and send the list up to the client.
Here we define a filterable array, and localize that array to be passed up to the client with the custom Gutenblock JS you’re already enqueuing to interact with Gutenberg (assuming you already have custom JS being enqueued)
This adds a new whitelistedBlocks key to our localized data, with an array of blocks that we want to whitelist, in this case, core/image and core/paragraph.
Now, if we did this properly, we should see our `gutenblockHelpers` object enqueued and accessible via window.gutenblockHelpers. You can see this in your browser console.
Use the whitelist on the client to filter Gutenberg
Now that we’re successfully passing up a list of whitelistedBlocks up to the client, we need some JS to use that list and tell Gutenberg to respect it.
Note the dependencies. I noticed that if this script is loaded without the proper dependencies, it will be enqueued too early and not work. I can’t explain all the reasons why these need to be the dependencies, but these are the dependencies I declared to get things working and without them, things went a bit wonky.
Next, we need to write the script that we’re enqueueing.
Note:, I’m using es6 which is being transpiled/minified by Webpack & Babel. I’m using the 10up theme scaffold which has Webpack configured and a build script that handles this.
let whitelistedBlocks =gutenblockHelpers &&gutenblockHelpers.whitelistedBlocks ?gutenblockHelpers.whitelistedBlocks : null;
const {unregisterBlockType, getBlockTypes} = wp.blocks;
/**
* We have to wait until all blocks have been registered before unregistering them.
*
* This is SUPER fragile and hacky and will likely cause things to break over time, so I suggest
* keeping an eye on Gutenberg to see how blacklisting/whitelisting evolves in the core plugin.
*/
wp.api.init().then( ( () => {
/**
* Loop through the blacklisted blocks and unregister them.
*
* The blacklist is added to the gutenblockHelpers global via wp_localize_script.
*/
if (
whitelistedBlocks.length &&
typeof unregisterBlockType !== 'undefined' &&
getBlockTypes !== 'undefined'
) {
getBlockTypes().forEach( block => {
if ( whitelistedBlocks.indexOf( block.name ) === -1 ) {
unregisterBlockType( block.name );
}
} );
}
} ) );
This script checks to see if there’s a list of whitelisted blocks passed up to the client from our localized data.
If there is, it waits for the wp.api to initialize, then if the core Gutenberg methods getBlockTypes and unregisterBlockType is available and the whitelist has items defined, it asks Gutenberg for a list of ALL registered blocks usinggetBlockTypes, then it loops through them and unregisters all that are NOT part of the list of whitelisted blocks.
At this point, if you try and interact with Gutenberg, the only blocks you should now have access to are the core/paragraph and core/image blocks.
Customizing the whitelist for Post Types or other conditions
It’s likely that you may want to customize the list of blocks that are allowed for certain post_types.
To do that, you just need to adjust your filtered list of blocks, like so:
add_filter( 'my_localized_gutenblock_data_filter', function( $localized_data ) {
global $post;
$localized_data['whitelistedBlocks'] = [
'core/image',
'core/paragraph'
];
// whitelist a custom block for a custom post type
if ( 'my-custom-type' === $post->post_type ) {
array_push( $localized_data, 'your-prefix/custom-block' );
}
// Remove the core/image block for a specific post type
if ( isset( $localized_data['whitelistedBlocks'][''core/image'] ) && 'my-custom-type' === $post->post_type ) {
unset( $localized_data['whitelistedBlocks'][''core/image'] );
}
return $localized_data;
}, 10, 1 );