Create a custom colour replace component
Older Article
This article was published 6 years ago. Some information may be outdated or no longer applicable.
In a recent course I recorded for Jamstack.training, I built an interesting React component that I’d like to share here. The course is Building an E-Commerce Application using Gatsby.
Jamstack.training is a portal dedicated to bringing quality video courses about the Jamstack to anyone wishing to learn about the various technologies.
Gatsby and React
For those who haven’t heard of Gatsby, it’s a React-based static site generator. Static site generators became popular alongside the Jamstack. They give developers tooling to create static HTML pages from templates written in a familiar framework or library. If you’re a React developer, you’ll feel at home with Gatsby because everything already looks familiar.
The component we’ll build uses hooks and other familiar React features.
The use-case
When browsing e-commerce stores for clothing, you’ll often find a t-shirt available in multiple colours. Photographing the same t-shirt in every colour is a tedious job. What if there were an automated way to swap out the original colour for a new one?
If we’re using Cloudinary to store our images, we can apply transformations, including colour replacement. This particular transformation grabs a colour (by name or hex value) and replaces it with another.
Here’s the example image we’ll work with throughout this tutorial:
https://res.cloudinary.com/tamas-demo/image/upload/w_300/Jamstack-training/winter-jumper
The original colour of this jumper is d79025 (a variation of orange/brown). Using that, we can swap the hex value to red (ff0000) by adding the e_replace_color transformation effect, which accepts three parameters: e_replace_color:new_colour_hex:tolerance:old_colour_to_replace:
https://res.cloudinary.com/tamas-demo/image/upload/w_300/e_replace_color:ff0000:10:d79025/Jamstack-training/winter-jumper
From the Cloudinary docs we can learn more about the
tolerance: The tolerance threshold (a radius in the LAB colour space) from the input colour, representing the span of colours that should be replaced with a correspondingly adjusted version of the target output colour. Larger values result in replacing more colours within the image. The more saturated the original input colour, the more a change in value will impact the result. (default: 50).
Now that we know this functionality exists, let’s build a React component that lists colour values and swaps the image colour on click.
Displaying the image
To display the image from Cloudinary, we can use their React SDK, which exposes an Image component and a Transformation component (we’ll use both).
First, install the SDK: npm i cloudinary-react.
We need to supply several props, including cloudName and publicId for the original image:
<image
cloudName="tamas-demo"
publicId="Jamstack-training/winter-jumper"
width="300"
crop="scale"
fetchFormat="auto"
quality="auto"
secure="true"
className="h-full w-300 md:mx-8 rounded-lg"
>
</image>
The rest of the props apply some extra transformations: scaling to 300 pixels, automatically reducing quality without affecting the human eye (
quality="auto"), and sending the most appropriate format to the browser (fetchFormat="auto"), which saves significant KBs over the wire.
We now have a way to display the image directly from Cloudinary. How do we apply extra transformations? That’s where the Transformation component comes in:
// added inside the <image /> component
<Transformation rawTransformation="{e_replace_color:ff0000:10:d79025}" />
Note that
rawTransformationlets us send a transformation directly, but we can also construct transformations using props. I’m using the former because it makes building the colour replace effect easier for our use case.
The colour replace component
Let’s look at how to create a separate component that lists colours, makes them selectable, and updates the image via the Cloudinary Image/Transform component when a colour is clicked. The code for this component is fairly compact:
// colour-component.js
import React from 'react';
function ColourSelect({ changeColour, getColourName, original }) {
const colours = [
{
name: 'yellow',
hex: 'ffff00',
},
{
name: 'indigo',
hex: '4b0082',
},
{
name: 'original',
hex: original,
},
];
function applyColour(e, colour, original) {
e.preventDefault();
changeColour(`e_replace_color:${colour}:10:${original}`);
}
function getColour(e, colour) {
return getColourName(colour.name);
}
return (
<div>
<p>Select a colour</p>
<ul>
{colours.map((colour) => {
return (
<li
key={colour.hex}
value={colour.hex}
onClick={(e) => {
applyColour(e, colour.hex, original);
getColour(e, colour);
}}
>
<span
className="cursor-pointer inline-block px-6 py-2 rounded-full m-1 text-black-800"
style={{ backgroundColor: '#' + colour.hex }}
>
</span>{' '}
{colour.name}
</li>
);
})}
</ul>
</div>
);
}
export default ColourSelect;
The component lists colours and makes them clickable. The tolerance value is hardcoded to 10. Feel free to parameterise that or change it to suit your needs.
Let’s see how we’d wire up this component:
// App.js
import ColourSelect from './colour-select';
const [colourTransformation, setColour] = useState('');
let [colourName, getColour] = useState('');
let [imgsrc, setImgsrc] = useState('');
const imageRef = useRef(null);
useEffect(() => {
setImgsrc(() => imageRef.current.element.src);
if (imageRef.current && imageRef.current.element) {
const observer = new MutationObserver((muts) => {
muts.forEach((m) => {
if (m.type === 'attributes' && m.attributeName === 'src') {
setImgsrc(() => m.target.src);
}
});
});
observer.observe(imageRef.current.element, {
attributes: true,
});
}
}, [imgsrc]);
// NOTE: we also need to add ref={imageRef} to the Image component
This might look tangled at first, but it’s not. We need a reference to the Cloudinary Image component because Cloudinary doesn’t provide a way to update the src attribute after changes are applied. We need to update the image programmatically once a transformation hits, and the MutationObserver lets us do exactly that by watching for DOM changes.
The action we’re taking is updating the src attribute of the Image component whenever a new colour gets selected from the colour-select component.
A working example
If you’d like to see things in action, go ahead and try it below.
You could add more colours to the list in the component and test with those.
Summary
Building beautiful e-commerce experiences becomes much simpler when you’ve got the right tools. The Jamstack approach is to use third-party services and APIs to create an enhanced experience. Here, we’ve seen how Cloudinary’s colour replace feature pairs with React to produce a reusable colour selector component.