At WebDevStudios (WDS), we’ve seen many iterations of page builders and page builder-esque tools over the years. I think it’s safe to say that the experimentation began with CMB2 and led us down an exciting path that included a handful of full-fledged page builders and our own custom-built page builder before we finally came to rely on Advanced Custom Fields (ACF) as a way to allow clients the freedom to edit, arrange, and customize their pages and posts. While ACF isn’t a page builder per se, its robust customization has given us the ability to present our clients with a similar level of customization found in strict page builders. Now… enter Gutenberg.
Here’s what the CEO of our company, Brad Williams, recently tweeted about it, “WordPress Gutenberg is not a page builder.”
WordPress #Gutenberg is not a page builder. Comparing Gutenberg, in its current iteration, to something like @BeaverBuilder is not an apples to apples comparison. Premium page builders aren't going anywhere and will always iterate faster than WP core dev.
— Brad Williams (@williamsba) March 29, 2018
While not a full-fledged page builder, Gutenberg does allow for the type of customization we have been building via ACF for several years. It certainly changes how that customization occurs, and part of our directive here at WDS has been to take what we have built in ACF and transfer it to Gutenberg. So, what is the first stop on the ACF-to-Gutenberg brain train? It’s converting our set of globally-used block options.
Part of what we include with all of our default ACF blocks is a panel of options to allow for some major visual customizations. These options include the usage of a background image, video, or color, customizing the font color, adding animation options and any custom CSS classes that a user may need to further tweak their block once it hits the frontend. As important as it was for us to make sure these options are available to use on all of our Gutenberg blocks, it was also important for us to ensure:
- The options are easy to include for developers
- The options exist in one central location to keep our code DRY
- The options work visually in a Gutenberg block the same way they work currently in an ACF block
My first step was to get completely overwhelmed. Digging into reusable Gutenberg components is no small task. After running through Zac Gordon’s Gutenberg Development course, I was comfortable building blocks and even components in the Inspector Controls panel, but this took on a whole new level. We needed conditional fields and, as stated earlier, a way to keep this code as DRY as possible to avoid simple mistakes from being made when attempting to include the Background Options on a new block.
Our options panel is broken down into separate parts, so it made sense to tackle these one at a time rather than trying to do all of the things in one sitting. We’ve broken down the various options into:
- Background Options
- Text Options
- Other Options
For the sake of this post, we’ll be focusing on Background Options. Before we get into the code, take a look at the file structure for our component.
First, we have
attributes.js. Like any regular Gutenberg block, our component needs attributes to save when its values are edited. We broke this out into an individual file because it would help keep our
index.js file clean, and it makes it easier to import our attributes into our blocks later on. Inside of our
attributes.js file we have:
What we’re doing here is setting our default attribute types and then exporting the entire set of attributes for use later on. Now if we ever need to update our Background Options attributes, we won’t have to edit them across all of our various blocks. We’ll just edit them once here and let them trickle down into our blocks wherever they are being used.
Next up is our
classes.js file. Neither
video.js are required by Gutenberg; these are files that will drive markup for functionality for us later on when we begin building our blocks. In
classes.js, we’re checking to see which Background Type is selected (image, video, color, or none) and adding a matching class to our block:
inline-styles.js, we’re doing a similar thing as in
classes.js . We’re checking to see which Background Type has been selected and then pulling in its value as an inline style. Specifically, this checks to see if we are using a background color or a background image and sets that value as an inline style on our parent container if either is found:
video.js file outputs the video selected when using the “video” Background Type. Since we can’t set a video to be a background element of a container the same way we can with a color or image, we have to output the video markup in the container and then position everything via CSS. Inside of our
video.js file is where we’ll keep the markup for our video output:
With that out of the way, it’s time to start building the functionality of our component. The first thing we need to do in our
index.js file is import any core WordPress and Gutenberg dependencies we’ll be using. These will vary slightly from component to component depending on what types of fields you’ll be using, but for us, we wound up with:
In order to keep our main file clean, as noted above, we broke out as many things as possible into individual files. Next, we have to import them into the
index.js file and export them so we can use them later on in our individual blocks. That last part will make sense once we get into creating a block. For now, let’s just assume you trust me, we import and export our internal dependencies like so:
Now, we can actually get into the nitty-gritty of building out the functionality for our Background Options component. First, we need to create a new function and export it so we can use it in our blocks:
Note that we’re passing in
props —this is important because we’re going to be using these to check, get, and set attributes throughout this function. Before we get into any of that, though, let’s set some variables for our various event listeners so we have something to hook into when we want to change the background color, background image, or anything else we’ll be customizing in this function:
Now, it’s time for the fun stuff! The first piece of the puzzle we’ll be building out is our Background Image Selector. Let’s kick this off:
Before we do anything, let’s check to see which Background Type has been selected. If the user has selected the “video” Background Type, then there’s no point in displaying the Background Image Selector.
Now that we know for sure whether or not we’re actually selecting an image, we can build out some further functionality. With this next bit of code, we want to check to see if we have a background image already set. If we do, then we don’t need to display the
MediaUpload component. But, if we’re starting fresh and want to select an image, then we definitely need that component handy. This utilizes the core Gutenberg
MediaUpload component that we imported way at the top of this file:
But, what if we already have an image? In that case, we want to display the image we’ve already uploaded as well as a way to remove it and replace it with a new image:
Since this comes after our first conditional check, we don’t need to check whether or not an image is already set. If we’ve made it this far in our function, then we know we already have an image saved and are safe to try and output the saved image.
Next, let’s prepare our Video Background Selector. The code here is going to look somewhat similar to the Image Selector we just built, but of course, there are little touches here and there that make it its own special thing. It begins just the same way by setting a
Just as we did with the image, let’s check which Background Type has been selected before proceeding:
Now, we need to see if we have a video saved already. This is pretty darn identical to our Image Selector above, except it’s specifying “video” as the value for “type” in the
We want our users to know which video they’ve uploaded to a particular block. So if a video is already set, we’re going to display a non-playing version of it in our component:
Finally, we want to be able to select a background color if the “color” Background Type is selected. Just like with the two previous examples, let’s start things by setting a
const for our Color Selector:
This particular selector is going to be a lot more streamlined, as we don’t have to worry about including an uploader or outputting anything visually in our component that doesn’t already come from core Gutenberg. First things first, let’s check our background type:
And if we know that we’re looking to set a background color, then we know that we need to display Gutenberg’s
We’re almost there! At this point, we’ve created the core functionality of all three of our Background Types: image, video, and color. But, how do we actually tell our component which one of these options we want to use? And how do we actually get any of this stuff to show up in our Inspector Controls panel?
To start, we’re going to veer from what we’ve been doing so far and simply
return rather than setting a
const. At this point, we’re actually beginning to output the contents of our component rather than building its individual parts. First, we need to start a
PanelBody , we want to have unique rows. One will house the Background Type select while the other will house the output for whichever value we have selected. Each of these will live inside of a core Gutenberg
PanelRow component. For our Background Type selector, we’ll need to build a selector dropdown and, lucky for us, we can use another core Gutenberg component! We also want this particular option to be visible regardless of the selected Background Type because we want to give our users the ability to change Background Type at any time.
Gutenberg really comes through and makes this easy for us. We’re simply creating a select dropdown and specifying the labels and values for each of our options. Remember all of the conditionals we set earlier in our function where we checked for
SelectControl is what sets the
backgroundType value and allows those conditionals to work. Make sure your value for each of these match up to what you’re going to be checking for elsewhere in your functions.
Lastly, we need a way to set our background image, video, or color. Remember when we created our
const variables above and did all of the work of setting our image, video, and color values? The reason we set those as
const variables the way we did is so we could create a cleaner output in this
return. Just below the
PanelRow we created for the Background Type selector, create another
PanelRow to output our settings:
You might be yelling at me right now that this won’t work—that we’re simply outputting all three of these individual Selectors with no rhyme or reason. Untrue! If you recall, when we set our
const variables above, we also checked to see if the selected Background Type matched the type of Selector we were about to display. Inside of all of those
const variables, we’re already checking to see which Background Type is set and either bailing if we don’t have a match or displaying the settings if we do. Checking inside of the
const lets us keep a very clean and streamlined render at the end of our function.
To see the full version of this file, check out the Gist here!
If you’re still with me… good! We just have a few more things to do before we can see this sucker when editing a block. So far, we’ve built the functionality out but have yet to add it to a block. We’ll do that next. I won’t go over how to build the entire block, because that’s a blog post for another day. If you want to see the end product of what we have in our Default Block as it stands now, you can view that here.
For our purposes, we’ll assume you have the block built already and are looking to add a Background Options panel to your block.
First, we need to make sure you’re importing
InspectorControls from Gutenberg core in addition to whatever other blocks you’re importing:
Remember back in our Background Options
index.js file when we imported our
video.js files then immediately exported them? We did that so we could easily and cleanly import all of our necessary Background Options pieces in one line:
With that one line, we’re gaining access to the entire
BackgroundOptions function we built as well as all of the goodies that we’re using to make our magic happen. Now, we need to actually begin including those things in our blocks. The first step is to include our attributes. Like I’ve said before, we strive to keep our code as DRY as possible and this was no exception. We didn’t want our developers to have to copy and paste a complete set of attributes every single time they created a new block, and we especially didn’t want to have to update a bunch of different files if our attributes changed at any point in the future.
The beautiful thing here is that with another sweet, single line we can import all of our attributes:
Next, we need to actually output our options panel. This happens inside of the edit function of our Gutenberg block and, again, by utilizing exports and imports we’ve made the process super easy. First, we check to see if our block is in focus and, if it is, we add our
Again, with that single line, we’re pulling in all of our
BackgroundOptions functionality and passing in our
props so we’ll have access to them in our component. This is really what makes the whole thing work. Without those few lines, we won’t have anything displayed in our
Finally, we need to output some markup so our block will listen for our Background Options and behave accordingly. For the next section, just know that you will have to output the same markup in both your edit and save functions to ensure your block functions properly and displays your options on both the frontend and backend.
We’re using a
section tag to wrap our block, and because we want to add a class and potentially inline styles based on our Background Options choices, we’ll need to tell our markup to do just that:
With those in place, we’ll now be able to see the background image or background color set for our block when using the “image” or “color” Background Type. But, what if we’ve selected “video” and have uploaded a beautiful MP4 to display in the background? Easy! We just need to display our video output using the import from the top of our block.
To see the full version of this file, check out the Gist here.
And that’s it!
I know, I know… “that’s it.” There’s a lot to digest here. A lot of this may have been overwhelming to read (it was overwhelming to write, too!), but I would encourage you to do exactly what I did when I started building this component. Do it wrong, get confused, and then start over. When I first started trying to build the Background Options component, I did it by starting with some code Jeffrey de Wit wrote. I had a hard time understanding what he was doing or why it was working. So, I broke everything down to its simplest form. Instead of going all out and building the entire Background Options panel, I started with just the Background Type select dropdown so that I could understand how values were passed between files and functions and just how everything worked at its core.
After the Background Options were taken care of, building the Text Options and Other Options panels were a bit of a breeze. They are far less complex than dealing all that came with building the Background Options panel, so they came together much more easily. As always, you can follow along with the progress of our WDS Blocks plugin and check out how we’re handling our other global components in our Github repo.
What brave, new worlds have you come across when getting to know Gutenberg? What have you built that seemed crazy at first, but wound up being completely worth the confusion and frustration that comes with trying to do something outside of your comfort zone? And, what are you looking forward to in Gutenberg as its inclusion in WordPress core gets closer and closer? Let us know in the comments below.