How to Add Color Settings to Your Custom Gutenberg Block

This post will go into depth in how to add color settings to your custom WordPress Gutenberg block. We will learn how to add the same color settings component, which features choosing from palette colors and custom color, as many of WordPress’ default blocks use.

This is what we will add to our custom block:

By using Gutenberg’s components we can very easily implement this palette/color-section to our own custom block. You can define the color settings to affect any color you want; background, text color, border-color or whatever else. You can also add as many color settings as you want to inside this panel.

Before we dive into the code, there’s some things you need to be aware of. Don’t skip ahead to the code as the next section will explain a lot why the code needs to do it like it does.

What you need to know first

The components for implementing palette/color settings are PanelColorSettings and withColors from the wp.blockEditor package. The component withColors is a so-called higher-order-component and needs to be implemented a little bit differently than simply outputting a normal component. I’ll go into a little more detail later on. But first we need to be aware of the basics in how Gutenberg handles block color settings.

How Gutenberg blocks handle color settings

There are certain rules for how Gutenberg handles color settings in blocks. If you’ve ever styled a Gutenberg-supported theme before you are probably already familiar with these rules. We’ll quickly go through them nonetheless because we have to handle this in our block code.

  • When a palette color is chosen, the node block element will get a class of a certain pattern. The class starts with “has-“, and then the palette’s slug comes after. The ending depends on what element the color should affect. For text color it ends with “-color“. For background-color it ends with “-background-color“. As an example, a block with a palette color “red” chosen as background-color will get the class “has-red-background-color“.
  • When a custom color is chosen (from the colorpicker), the node block element will get inline style with the hex code. For example a custom color #DD0000 chosen for background color will get the attribute “style="background-color: #DD0000;“.

When we implement color settings for our block we will need to implement the correct class and inline style. We’ll do that at the end of this tutorial.

How to work with withColors

As mentioned earlier withColors is a higher-order-component. What that basically means it’s a component that takes a component and returns a new component. In the returned component we get useful props from the higher-order-component. To put it simply, we will use withColors to return our own component for our custom block. Our block component gets the necessary props for working with colors from withColors.

The component withColors handles state and a lot of functionality for working with colors. And we get a lot of automation in this process. This is very handy for figuring out if the chosen color is a palette color (we need to add a class) or a custom color (we need to add inline style). withColors simplify a lot of the process of storing the chosen color, whatever it is, into our block’s attributes. Speaking of attributes..

Attributes and withColors

Obviously your block needs attributes to store the chosen color. In order to benefit from withColor’s automation for storing the correct color you actually need to define two attributes for each color setting. One for storing the palette color’s slug, and another for storing the hex code. There are some rules though.

Say you want to add a color setting for the block’s text color – so you decide to define an attribute aptly named “textColor“. You will then need to define another attribute in the pattern “customYourOriginalAttribute“. In this example the second attribute will need to be named “customTextColor“. Mind the camelCase (capitalization) here. Following this pattern withColors will automatically:

  • If a palette color has been chosen, the attribute “textColor” will contain the palette’s slug.
  • If a custom color has been chosen, “customTextColor” will be populated with the hex code.

These two work in tandem. If a custom color is chosen, textColor will automatically be undefined, and vice versa.

And finally, one last thing to remember: you will not need to use setAttributes() for updating your color attributes! The provided props from withColors include a function that automatically updates your attributes for you. All you need to do is to pass this function to the onChange event to the PanelColorSettings component, and your attributes will automatically be updated.

Ok, it’s time to see all of this in practice!

Implementing color settings in a custom block

To start things off I have a pretty useless custom block that does nothing else than display a hardcoded text. This just makes it easy to separate out what we need to code for adding color settings. I have a tutorial series on how to create your own custom blocks if you’re interested in learning more.

Note: I’m writing all the code in ES6/ESNext. This includes arrow functions which require special care in your Babel/webpack setup. If you are getting errors on some of the code below, follow my guide in how to set up Webpack and Babel for ES6/ESNext or adjust the code to not use “experimental syntaxes”.

This is my basic custom block before doing anything with color settings:

const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;

const BlockWithColorSettings = (props) => {
	return(
		<div>
			PanelColorSettings Demo
		</div>
	);
}

registerBlockType('awp/colorsettings', {
	title: __('Color Settings Demo'),
	icon: 'carrot',
	category: 'common',
	edit: BlockWithColorSettings,
	save: (props) => { 
		return(
			<div>
				PanelColorSettings Demo
			</div>
		);
	}
});

It’s pretty basic. Note that the edit function is simply referring to a separate component, BlockWithColorSettings, instead of writing it inline. This makes it easier to implement withColors later.

Okay, time to implement color settings to our block! As an example I want to set up text color.

Adding attributes

As mentioned earlier we need to define two attributes for each color setting. And we need to take extra care in their naming. I want to add a text color attribute, so I’m naming the attribute textColor. Which means I will also add an attribute customTextColor. Both should be of type string.

...
registerBlockType('awp/colorsettings', {
	title: __('Color Settings Demo'),
	icon: 'carrot',
	category: 'common',
	attributes: {
		textColor: {
			type: 'string'
		},
		customTextColor: {
			type: 'string'
		},
	},
	edit: BlockWithColorSettings,
	save: (props) => { 
	...

Our attributes are ready for storing the block’s text color setting. Now it’s time to implement withColors and PanelColorSettings.

Implementing withColors

As mentioned earlier withColors is a higher-order-component that should take a component to return. We obviously want it to return our edit component, BlockWithColorSettings. But as parameter to withColors we provide an object.

In the object passed to withColors we need to tell withColors what attribute we want to use for storing the color and what kind of element it will color (in our case text color, which means CSS rule “color”). Informing about the CSS rule makes sure that the class names returned are correct. Because this is text color we want class names like “has-<palettecolor>-color”.

First some destructuring at the top. withColors resides in the wp.blockEditor package.

const { withColors } = wp.blockEditor;

We’ll change the edit function into:

	...
	attributes: {
		...
	},
	edit: withColors({textColor: 'color'})(BlockWithColorSettings),
	save: (props) => { 
		...

With this code our component BlockWithColorSettings will receive some additional props:

  • props.textColor: Is an object that consists of all information about the chosen color. If a palette color was chosen it will store properties for hex code, palette slug, class name, and more. If a custom color was chosen the object will contain the hex code. The hex code is always found in the property color. And the class name, (only if a palette color was chosen) will be set in the property class.
  • props.setTextColor: A function that will update our attributes for us. We provide this for the color settings’s onChange event as we’ll see later. The function will update both textColor and customTextColor attributes. Because we followed the naming rules it will automatically update customTextColor even though we never provided this attribute name.

Note that the “set”-function provided as prop will follow the rule: “setYourAttributeName“. Because we provided textColor, the function is named setTextColor. If we instead named our attribute awesomeColor and provided this in the withColors object, the set-function would be named setAwesomeColor().

Implementing PanelColorSettings

The next step is implementing the actual Inspector section. To do this we add PanelColorSettings inside a InspectorControls component. Because React requires everything to be inside one root node, we also use Fragment (from wp.elements) to wrap everything inside.

First some destructuring at the top of the file:

const { Fragment } = wp.element;
const { InspectorControls, PanelColorSettings, withColors } = wp.blockEditor;

And we update our BlockWithColorSettings component into something like this:

const BlockWithColorSettings = (props) => {
	const { textColor, setTextColor } = props;  // Props received from withColors
	return(
		<Fragment>
			<InspectorControls>
				<PanelColorSettings 
					title={__('Color settings')}
					colorSettings={[
						{
							value: textColor.color,
							onChange: setTextColor,
							label: __('Text color')
						},
					]}
				/>
			</InspectorControls>
			<div>
				PanelColorSettings Demo
			</div>
		</Fragment>
	);
}

As you can see in line #2 we destructure the props received from withColors, textColor and setTextColor. Keep in mind that props.textColor is the object received from withColors, and props.attributes.textColor is the attribute. We have no need to refer to the attribute though.

As props to the component PanelColorSettings we can provide a title for the section (title in the collapsible box in Inspector). The important thing here is the prop colorSettings where we need to provide an array of color setting objects. For each color setting (we currently only have one) we need to provide some properties. The property value expects the currently chosen hex code (even though a palette color was chosen). This is provided for us in the textColor prop, inside the property color. For the onChange property we simply provide the “set” function provided by withColors, setTextColor. And finally we should give a label so that the user knows what element this color will affect. It will appear right above the area of choosing a color.

This is how our component appears in Gutenberg editor right now:

It is now successfully updating our attributes when choosing colors. You can see that it remembers your color choice when saving the post.

However, nothing visually happens when you change colors. The color choice is stored in the attributes, but no color change happens in the editor, nor when previewing the post. This is because we need to add code for the block’s classes and styles. We need to do this for both edit function (which is for the editor) and save function (frontend). That’s the next step.

Handling class and inline styles in edit

We need to build the block’s node class and style attributes according to the chosen color setting. Luckily with withColors we get some automation in this. Remember that props.textColor is an object that contains all information we need, including the class name.

We could do something like this:

...
const BlockWithColorSettings = (props) => {
	const { textColor, setTextColor } = props;  // Props received from withColors
	
	let divClass;
	let divStyles = {};
	if (textColor != undefined) {
		if (textColor.class != undefined) {
			divClass = textColor.class;
		} else {
			divStyles.color = textColor.color;
		}
	}
	
	return(
		<Fragment>
			<InspectorControls>
				...
			</InspectorControls>
			<div className={divClass} style={divStyles}>
				PanelColorSettings Demo
			</div>
			...

At line #20 we apply the critical class and inline style to the root node of our block. Before that we build the class and inline style attribute by checking the props.textColor object.

After this change your custom block should now be fully functional in the editor. Whenever you switch color the block will change the text color. Awesome! The final step is doing this for the save function as well, so that we get these classes and styles in frontend as well.

Handling class and inline styles in save

The concept of building the class and inline styles and applying them to the root node is the same in save as in edit. But there is a crucial difference. In save we don’t have the props provided by withColors. And we cannot add higher-order-components to the save function. So in the save function all information we have are the attributes.

It’s a good rule of thumb to avoid hardcoding “has-” classnames. What if WordPress decides to change this rule in the future? Luckily we have a function that can help us generate the proper classnames for us: getColorClassName().

Before we forget, let’s destructure it. It’s also in the wp.blockEditor package.

const { InspectorControls, PanelColorSettings, withColors, getColorClassName } = wp.blockEditor;

Using the getColorClassName() function requries two parameters. First a string for the CSS rule. Because our color setting is for text color, we provide ‘color‘. This tells the function that it should return a classname of “has-something-color” and not “has-something-background-color” for instance. As second parameter we need to provide the value of the attribute.

The style attribute is simply built by setting “color” to the value of the attribute customTextColor, if it’s defined.

save: (props) => { 
	const { textColor, customTextColor } = props.attributes;
	let divClass;
	let divStyles = {};
	if (textColor != undefined) {
		divClass = getColorClassName('color', textColor);
	}
	if (customTextColor != undefined) {
		divStyles.color = customTextColor;
	}
	return(
		<div className={divClass} style={divStyles}>
			PanelColorSettings Demo
		</div>
	);
}

And of course, don’t forget to apply the class and style onto the root node of your block; as in line #12.

PS: If you are testing your block in the editor while writing this code, you will now get a block error. This happens because we now have changed the output for the save function and whatever you have saved previously is in conflict. You will have to remove the block and re-add it again.

After this change, your block should now properly apply the chosen text color in frontend as well.

And that’s it! You have now successfully implemented color settings to your block. If you’re interested in adding multiple color settings (not just text color), read on.

A note on multiple color settings

By now you should be able to implement multiple color settings. You may want to add settings for background color, text color, border color, or whatever else, for your block. In this section I’ll do a quick overview of what we need to do in order to implement multiple color settings.

Let’s assume that I want to add background color to my block as well.

First I need to define a new attribute creatively named backgroundColor. I define another attribute customBackgroundColor as well.

In the edit function I change the object provided to withColors as such:

withColors({textColor: 'color', backgroundColor: 'background-color'})

This tells withColors that my textColor attribute is for the CSS rule “color” (for text color), and the attribute backgroundColor is for the CSS rule “background-color“. withColors will automatically recognize and update the customTextColor and customBackgroundColor attributes as well.

In the PanelColorSettings component, I provide another object to the array to the prop colorSettings. Like so:

<PanelColorSettings 
	title={__('Color settings')}
	colorSettings={[
		{
			value: textColor.color,
			onChange: setTextColor,
			label: __('Text color')
		},
		{
			value: backgroundColor.color,
			onChange: setBackgroundColor,
			label: __('Background color')
		},
	]}
/>

With this you should get two separate color settings inside the Inspector box for color settings.

The last step is building the appropiate class names and styles in both edit and save. This is a pretty simple step as it’s just building a string or style object correctly. Remember that your class name must support multiple color classes (e.g. if both text color and background color was chosen). Simply add a space between each class name.

PS: If you need to handle a lot of class names for your block, you might benefit from installing the classnames package. Pretty much all components in Gutenberg uses this library to more easily combine classnames.

Conclusion and full code

You should now have learned how to implement color settings in your custom block. I hope this was of some use to you! I had to add this feature to my project, and I really couldn’t find any information or good documentation. So this is the result of consolidating everything I learned about this topic, after a lot of trial and error.

Here is the final code, alltogether, for the example custom block with text color setting:

const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
const { Fragment } = wp.element;
const { InspectorControls, PanelColorSettings, withColors, getColorClassName } = wp.blockEditor;

const BlockWithColorSettings = (props) => {
	const { textColor, setTextColor } = props;  // Props received from withColors

	let divClass;
	let divStyles = {};
	if (textColor != undefined) {
		if (textColor.class != undefined) {
			divClass = textColor.class;
		} else {
			divStyles.color = textColor.color;
		}
	}
	
	return(
		<Fragment>
			<InspectorControls>
				<PanelColorSettings 
					title={__('Color settings')}
					colorSettings={[
						{
							value: textColor.color,
							onChange: setTextColor,
							label: __('Text color')
						},
					]}
				/>
			</InspectorControls>
			<div className={divClass} style={divStyles}>
				PanelColorSettings Demo
			</div>
		</Fragment>
	);
}

registerBlockType('awp/colorsettings', {
	title: __('Color Settings Demo'),
	icon: 'carrot',
	category: 'common',
	attributes: {
		textColor: {
			type: 'string'
		},
		customTextColor: {
			type: 'string'
		},
	},
	edit: withColors({textColor: 'color'})(BlockWithColorSettings),
	save: (props) => { 
		const { textColor, customTextColor } = props.attributes;
		let divClass;
		let divStyles = {};
		if (textColor != undefined) {
			divClass = getColorClassName('color', textColor);
		}
		if (customTextColor != undefined) {
			divStyles.color = customTextColor;
		}
		return(
			<div className={divClass} style={divStyles}>
				PanelColorSettings Demo
			</div>
		);
	}
});

One comment on “How to Add Color Settings to Your Custom Gutenberg Block”

Leave a comment