light

Thieving Your Way to a Dynamic Colour Theme in React

Guy holding a phone looking at a colour palette on the wall

Dynamically set the colour palette of your React components with color-thief-react.

Time to read: ~2 minutes


I always thought it was really cool how some applications were able to dynamically set the colour of components on their page based on an image that was being displayed.

I wanted to get this kind of effect myself, and luckily discovered a javascript library, Color Thief React, that provides components and hooks for Color Thief, making it super easy to use Color Thief from a React application. This article will cover how to use the library as well as demonstrate a little demo of what you can get out of it.

There are two main components included in this package: Color and Palette. Color will get you a single dominant colour from a source image while Palette will return a number of colours. Both of these components are also available as hooks. If you want to take a deeper dive into the details about this package, you should check out the README in the github repo.

Getting Started

I'm starting off with a bare bones create-react-app with all the extra fluff removed.

Next we want to install the package.

npm i -S color-thief-react

Matching the Colour of a Source Image

I'm a big fan of react hooks and it makes things super simple. Inside a functional component, it's as easy as adding the following snippet.

import imgSrc from './images/{whateverImage.ext}'; import { useColor } from 'color-thief-react'; function ColourMatcher() { const { data } = useColor(imgSrc, 'hex'); return ( <div> <img src={imgSrc} /> <p style={{ color: data }}>My colour matches!</p> </div> ); }

Here we grab an image from our local directory (alternatively you can pull an image from an online source), and use it inside the useColor react hook along with our desired colour format. We can access this colour data using data. Now we can use the data to specify the colour for our elements!

And here is what things should look like if you did things correctly:

Image with dominant pink colour and text with a matching colour

The useColor hook also provides access to two additional properties: loading and error. Pull in react's useEffect for monitoring the status of the loading property and you can add some neat reactiveness to your application!

const { data, loading, error } = useColor(imgSrc, 'hex'); useEffect(() => { if (loading) console.log('still loading...'); else if (error) console.error(error); else console.log('got a color!', data); }, [loading]);

Matching the Palette of a Source Image

Now if you need more than a single colour, you might want to extract a palette from the image instead. Check out the example below!

import { usePalette } from 'color-thief-react'; function PaletteMatcher() { const numColours = 3; const colourFormat = 'hex'; const { data, loading, error } = usePalette(imgSrc, numColours, colourFormat); return ( <div> <img src={imgSrc} /> { loading ? <p>Loading...</p> : (data.map(colorData => { return (<p style={{ color: colorData }}>My palette matches!</p>); })) } </div> ); }

And voila! Color thief was able to extract the top 3 colours from the image so that I could use it to colour the text elements (one of the colours was super bright so you may not be able to see it).

Image with pink, red, and orange colours and three lines of text with colours matching the palette of the image

Demo

With this, you can achieve some pretty cool dynamic theming with colours that match your media. Check out this demo which selects colours from the palettes of the album covers of some of my favourite music artists.

Demo of color-thief switching between different album covers with accompanying html elements that have matching palette

Wrapping Up

So now you see how easy it is to get this working and how it can help to add some cohesivness to the elements accompanying your media in your application. To build on this, I'll be looking for a solution that ensures that text has appropriate contrast with its background. I'll be discussing this in one of my posts in the future so stay tuned for that!

Thanks for reading, now go build some stuff!

Feel free to reach out!


Want to show your support?

Copyright © 2021 - 2022 Andrew Grass All rights reserved.