just some notes.

Making a custom color picker

May 20, 2020

Colors are fun. Especially when you can choose your own. You’ll need a color picker for that though. Luckily, we can easily create a custom one ourselves with react.

While you can use the native color picker that the browser offers, we’ll be using the library react-color. It provides us with a couple of helpful components, which speed up the progress of making a picker. So first we install react-color.

yarn add react-color

Once installed we can immediately started coding. The library is built in such a way that it offers a higher order component that you need to wrap around a component of your own. The higher order component provides you with functionality we’ll go through later. First let’s import the HoC and make a component to wrap around.

import {CustomPicker} from "react-color"

class ColorPicker extends React.Component {
    render() {
        return (
            <div>
            our amazing picker
            </div>
        )
    }
}

and then let’s wrap the CustomPicker higher order component around it.

const ColorPickerWrapped = CustomPicker(ColorPicker);

If at this point you console.log the this.props of the ColorPicker component, you see that we’ve just got a bunch of props.

color: Object
hsl: Object
hex: "#22194d"
rgb: Object
hsv: Object
oldHue: 250
source: undefined
onChange: function () {}

There is a color prop we’ll discuss in a minute, but there are also an hsl prop, a hex prop, a rgb prop and an hsv prop. These are all color formats that may or may not be useful too you. The great thing about the library is that it provides you with all of them, and is fine with you providing only one.

Let’s get to that color prop. We need a piece of state to keep track of the color we’re selecting in our yet to make picker. We’re going to keep that state not in the ColorPicker component itself, but we’re going to lift it a component higher.

export default function App() {
    const [color, setColor] = useState("#FFF000");
        return (
            <div
            style={{
                position: "fixed",
                width: "100%",
                height: "100%",
                margin: 0,
                padding: 0,
                background: backgroundColor
            }}
            >
                <h1>A Custom Color Picker with react-color</h1>
                <ColorPickerWrapped
                    color={color}
                    setColor={setColor}
                />
            </div>
        );
}

As you can see we created a new component that keeps the color in a piece of state. We pass that state as a prop to our earlier wrapped ColorPicker. The reason that we keep this state in a parent component, is that the prop needs to passed to the wrapping component to do its converting magic. If you check the props again of the ColorPicker you can now see that the default hex color we provided in the state has now been converted to rgba, hsl and hsv for us. We still can’t change the color though. Let’s include one of the helper components from react-color. We can start with a simple one: a simple input to change the hex code of the color.

var { EditableInput } = require("react-color/lib/components/common");

<EditableInput
  value={this.props.hex}
  onChange={data => this.handleChange(data.Hex)}
/>

The EditableInput component needs at the minimum these two props: a value prop to display what the value of the input field is, and an onChange handler to handle the new values that are typed into the field. We can use the hex prop from the higher order component for the value. For the onChange handler we need to create a little function.

handleChange = data => {
    this.props.setColor(data);
};

It just takes the Hex property of the data object and then calls the setColor function we passed down from the parent component. That will change the color in the state, which is then passed down again and correctly displayed in the input field. We now have a very barebones custom color picker!

Still, this is no fun. We need a saturation picker. Let’s import it and set it up.

<div
    style={{
        float: "left",
        width: "200px",
        height: "200px",
        position: "relative"
      }}
>
    <Saturation
        {...this.props}
        onChange={this.handleChange}
        pointer={Picker}
    />
</div>

Okay, this one has some more meat on the bones. First off, you need to wrap this component in a div with position: relative. This is because the Saturation component is positioned absolute. Next the component needs the props the wrapper provides. So might as well pass ‘em all down by destructuring this.props.

The onChange should seem familiar. You can use the same helper function, but no need to specifically pass a property of an object. Just pass it all! Then there is the pointer prop. It is optional and you can leave it empty. However, you may also pass a custom component to configure your own little picker. Let’s do that quickly.

function Picker() {
return (
        <div
        style={{
            width: 20,
            height: 20,
            borderRadius: 20,
            background: "rgba(255,255,255,0.2)",
            border: "1px solid white",
            boxShadow: "0px 2px 4px rgba(0, 0, 0, 0.25)",
            boxSizing: "border-box"
        }}
        />
    );
}

Ok done. Save the file and now behold a very cool saturation picker. The fun thing is that you should see your hex input field be updated as you drag the picker across the saturation picker. And vice-versa.

In the same vain we can now also add a hue picker.

<div
    style={{
        float: "left",
        width: "200px",
        height: "20px",
        position: "relative"
      }}
>
    <Hue
        {...this.props}
        onChange={this.handleChange}
        pointer={Picker}
      />
</div>

You’ll notice there isn’t much different from the saturation picker. While we’re at it let’s also include an alpha picker. This will let us select a see-through shade :)

<div
    style={{
        float: "left",
        width: "200px",
        height: "20px",
        position: "relative"
      }}
>
    <Alpha
        {...this.props}
        pointer={Picker}
        onChange={this.handleChange}
      />
</div>

Awesome! We’ve now created a pretty cool color picker. Let’s put it into practice. How about we use the color picker we just created to change our background color. First we’ll need to set up some state in the parent component, and some styling.

export default function App() {
const [color, setColor] = useState("#FFF000");
const [backgroundColor, setBackgroundColor] = useState("#FFFFFF");
return (
    <div
    style={{
        position: "fixed",
        width: "100%",
        height: "100%",
        margin: 0,
        padding: 0,
        background: backgroundColor
    }}
    >
    <h1>A Custom Color Picker with react-color</h1>
    <ColorPickerWrapped
        color={color}
        setColor={setColor}
        setBackgroundColor={setBackgroundColor}
    />
    </div>
    );
}

So there is now some state, a function to set the background color and we’ve added styling based on the state.

Since we’ve got those fancy pickers we don’t want to suffice for just a regular ol’ hex color, we want a rgba color with alpha. We know that react-color provides the values we need for that. So basically the only thing we need to do is create a button that confirms our color choice, and then have a helper function that takes the rgba value from the higher order component, which is then passed into the setBackgroundColor function as valid css.

selectColor = color => {
    const rgba = `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`;
    this.props.setBackgroundColor(rgba);
};

<button onClick={() => this.selectColor(this.props.rgb)}>
    Select a color
</button>

There we go. The rgb prop from the HoC provides us with an object that contains the seperate values for the r, g, b and a. Our styling expects an rgba value that is formatted like: “rgba(r,g,b,a)”, so all we need to do is format it like that using a template string.

With those additions in place we have completed our custom color picker. You may now include your own styling, fantasy and use it in the wild.

Full code: https://codesandbox.io/s/just-a-custom-color-picker-uu6v3?file=/src/App.js