Customizing React Color’s Sketch Picker to display recently used colors

Square composed of 100+ triangles, partly colored in shades of teal beside a color selector tool with a hue slider and 15 swatches of colors.

Whenever I gather materials to start a new painting, I always reach for a palette to hold the paints I’ll be using. Most often it’s a well-used piece of palette paper covered with the last painting’s color scheme. The palette makes it easy to switch between core colors without having to remix a frequently used color for each new brush stroke.

For a recent art-themed React project, I wanted to emulate the benefits of a palette and allow users to easily access their recently used colors as they ‘painted’ a digital canvas. In this post, we’ll build out this functionality by customizing React Color’s SketchPicker component. We’ll also explore other customizations available through the SketchPicker API that make the component a flexible and helpful tool.

You can check out the frontend repo for the project this post is based on here if you’d like more context. Code samples in this post have been simplified so we can focus on our color picker customization.

Setting up SketchPicker with Core Props

For this demo, we’ll work with the pre-built SketchPicker component, so let’s install the package in our app’s directory with the command npm install react-color — save.

Next we’ll import the SketchPicker into a parent component in our app called StudioPage, which will also render the Canvas component our users will be painting. We’ll also set up local state in the parent component to manage the active color on the color picker.

You’ll notice that SketchPicker is receiving two props from the parent component. Each React Color picker is prepared to receive at least two props, color and a choice of either onChange or onChangeComplete.

As the React Color docs explain, color controls the color picker’s active color. In the code snippet above, we’ve set an activeColor value in local state that will display as the selected color when the SketchPicker loads. For your color prop, you can pass a hex code as a string or an object containing either RGB or HSL values. In this demo, we’ll work with hex colors.

Since the color picker’s selected/active color is controlled by the value of activeColor in our parent component’s state, we need a way to update activeColor each time a user selects a new color on the SketchPicker.

To achieve this, React Color lets you pass the color picker a function to execute when a color change occurs. If you want the function to fire on every change (including drag events), pass your callback function via a prop called onChange. If you want it to execute only once per core change, then pass your callback to onChangeComplete.

Note that if you use onChangeComplete, you can drag your color selector (via the hue slider or saturation block) to a new position, but the selector will not move with your cursor as you drag it. It remains stationary until you release the drag motion. Then the selector will jump to the new position. The selector does follow your pointer in a visual dragging motion if you use onChange.

We’ll name our callback function handleChangeComplete. Whatever callback you pass to onChange or onChange complete can receive both the selected color and the event as arguments. For this demo, we only need color details, so we’ll just pass in color to our callback.

Inside this callback, we’ll update the value of activeColor in our local state and set it equal to color’s hex value by calling color.hex. You can also access the RGB or HSL values of the selected color via color.rgb or color.hsl. Hex values can come through with lower or uppercase letters, so for consistency, we’ll save the values in uppercase format. This becomes important later when we build out our recent colors tracking feature.

Customizing the Color Picker

Some of the color picker options come with their own APIs, which are outlined in the React Color docs. SketchPicker lets you pass additional props if desired, including:

disableAlpha
Alpha describes the opacity of a color. If you don’t want to display alpha values on the color picker, pass disableAlpha the boolean value true (set to false by default). Make sure to pass true as a boolean value, not a string. Since opacity did not factor into the app I was working on, I chose to disable this feature.

width
The SketchPicker’s width is set to 200px by default, but you can pass in a new value to change the width. I chose to use a percentage so the picker would scale with its container.

className
If you want to further adjust the component’s style, use the className prop to pass in your own styles. Since we don’t have access to the picker’s styles, you might need to override them in your CSS if you want to change already set values.

onSwatchHover
You can customize the hover behavior of the SketchPicker’s preset color swatches. Add an onSwatchHover prop to your picker and pass it a callback function that executes your desired hover action. The callback function will be able to receive both color (i.e. the selected color) and event as arguments.

presetColors
The SketchPicker displays 15 color swatches by default, but you can pass your own set of colors as either hex codes or RGB/HSL objects. You can also pass in more or less than 15 options. We’ll use this prop to transform the color swatches section into our own paint palette that tracks recently used colors.

Tracking Recently Used Colors in our Picker

In the code sample above, you may have noticed we’re passing this.state.presetColors as the presetColors prop to our color picker.

In our parent component’s local state, we’ll add presetColors and set it equal to the default presetColor values provided by the SketchPicker.

These 15 default colors provide a nice starting palette, but we’ll update this slice of state with each new color used so that it becomes a rotating color palette displaying recently used colors.

In the app I worked on, a user can click on shapes within an SVG, our <Canvas /> component, to add colors. Each click triggers a callback action, handleClick, in StudioPage that accepts the shape’s id and the currently active color. It then dispatches an action via a prop called addColor that updates the shape’s color. Understanding this user interaction is relevant because it helps determine when a color becomes ‘recently used’. In this case, we can say a color becomes ‘recently used’ when a user adds it to a canvas shape, or in other words, when a click event happens on the canvas.

Now let’s take a look at the handleClick function we’re passing to our Canvas component.

As mentioned above, this function accepts a shape/polygon id and the active color from our color picker. The first line handles updating our canvas and isn’t our focus here. Instead, let’s walk through the logic in the rest of the function.

Each time a user fills a shape with a color and triggers this function, we only want to add the color added to our swatch collection if it isn’t already present. To achieve this, we’ll use a conditional:

if(!this.state.presetColors.includes(activeColor))

Remember when we made sure our active color was stored as a hex value in uppercase format? Since we know all our colors in our presetColors array are stored in the same format, we can check to see if the array contains the color that was just used on our canvas. If it doesn’t, then we move into the block to update state.

setState is a built-in React function that lets us update our state. Because we’ll need to build on the current values in our presetColors array, we’ll pass in a callback function to setState. (If you’re updating state based on previous state, it’s best practice to use this format because of setState’s asynchronous nature). When we pass a function to setState, this function can access our component’s state (often called previous state) as an argument.

We’ll pass in an argument named state to represent this previous state. Keeping with best practices, we’ll avoid mutating state directly and create a copy of our presetColors array for editing.

const colors = state.presetColors.slice(0)

Next we’ll add the used color to the beginning of our array and remove the last color so our array stays at a length of 15 colors.

colors.unshift(activeColor)
colors.pop()

Finally, we’ll return the updated value of presetColors, updating our local state. Because our SketchPicker relies on the value of presetColors, it will be re-rendered and the row of 15 swatches will now include the just-used color. Just what we want!

Wrapping Up

Software engineer interested in the intersection of tech, design+art, and social innovation