How to add text colour in Contentful’s Rich Text

I have just finished producing a website for a client. They had wisely opted to build the website with modern Jamstack principles and architecture. The site required a high level of customizability, and as my preferred headless content management system (CMS) at the moment is Contentful — that is what I decided to use.

One of the features of Contentful is that it allows you to write in rich text. This is a relatively new featured, being implemented to supplement and improve the previous markdown implementation. Contentful kindly wrote packages that you can easily add into your project to get this functionality up and running in a basic configuration very efficiently.

However, they don’t allow any options for customising the colour of the text that you are writing. If you want to create nice coloured headers or add emphasis with colour, you have to predetermine that beforehand in the CSS and not on the CMS side. For my client, this was a problem because they had multiple use cases requiring more than just a single alt colour for the text.

In this short guide, I’m going to run through my quick and dirty implementation of colouring text within Contentful itself. Let’s dive in.

How to indicate a colour

The first problem was deciding on how to pass the information that a piece of text needed to be a different colour to the website itself. I toyed around with making italics a different colour, but that didn’t leave them with any customisation capabilities. I also considered just making it parse HTML directly, but that would have left the client writing code — something which they were paying me to do.

Even just adding in a "<span style="color:red" >COLOURED TEXT</span> could end up causing a whole bunch of headaches down the line.

In the end, I finally settled on an idea that was inspired by the way you add links or photos in markdown. After a quick search (one Google query) to see if somebody had implemented this already in any markdown editors, but I couldn’t find any.

Whilst not the most straightforward thing for a non-technical person to use, the pattern was better than HTML, in my opinion. Example below…

A Super Cool Header With (Red Text)[#741B1B]

An advantage of this method of implementation was that it allowed me to use a regex to search strings for those patterns and then quickly extract them, wrap them in a <span> and stick the appropriate colour hex in style.

How to implement the Rich Text Colour

Add the packages

yarn add @contentful/rich-text-types @contentful/rich-text-react-renderer

Implementing the base Rich Text component

You can modify Contentful’s Rich Text packages by adding your own options. This allows you to provide components directly to the renderer and gives your greater control over the display. Contentful’s Rich Text will enable you to embed other pieces of content within the Rich Text. For example, you might have another blog post that you want to link to and have created an HTML/JSX component to show those within the blog post. You could embed that within the Rich Text then extract that content type in React and render a custom blog post component within the Rich Text.

It is quite powerful and useful.

I could have probably implemented a coloured text content type and then embedded those as inline elements (yes it suppers inline elements in the Rich Text editor). But this would have added unnecessary complexity in my eyes and would require the end-user to go to a particular content type to create the coloured text and then add it to the Rich Text editor.

A super simple setup might be as follows.

import React from 'react'
import { BLOCKS, MARKS, INLINES } from "@contentful/rich-text-types"
import { documentToReactComponents } from "@contentful/rich-text-react-renderer"
const options = {
renderMark: {
[MARKS.BOLD]: text => <span className="font-bold">{text}</span>
},
renderNode: {
[BLOCKS.HEADING_1]: (node, children) => <h2>{children}</h2>
}
}
const RichText = React.forwardRef(({ text, className }, ref) => (
<div ref={ref} className={className}>
{documentToReactComponents(text, options)}
</div>
))

Adding the colour functions

What I discovered by logging the value of children above is that it always passes an array. However, sometimes that array contains a string if there are no marks on the text. But if bold, italics or underline have been used then the array is an object. The array can easily be fill of multiple different strings and objects if the block of text is long enough.

I implemented this through a function that takes the children array as its sole argument and then maps through the array to create a new array with any changes that were needed.

const addColour = (children=[]) => {    // flatMap returns a flattened array - very useful
const mappedChildren = children.flatMap(child => {
if (typeof child === "string") {
// the regex that handles parsing the actual string and extracting the text
const matches = child.match(/\((.+)\)(?=\[(#\w+)\])/)
if (matches) {
return createSpanFromMatches(matches, child)
}
}
if (typeof child === "object") {
const content = child.props?.children
const className= child.props?.className
const matches = typeof content === "string" && content.match(/\((.+)\)(?=\[(#\w+)\])/)
if (matches) {
return createSpanFromMatches(matches, content, { className } )
}
}
// make sure to always return the content if there is no match to the regex
return child
})
return mappedChildren
}

I know this isn’t the driest or the cleanest code, but it is understandable and robust enough for the purpose — tugs at his collar.

The regex

/\((.+)\)(?=\[(#\w+)\])/

This regex is just matching any content between a set of regular brackets () that is immediately followed by a group of square brackets [] where the first character is a hashtag. I could have narrowed this down and required that it be a pattern that exactly fits a colour hex, but I ran out of time. If I do this again for another project, I will probably add that in.

I happen to be a big fan of regex, not sure why but I like writing them. It is possible because there are some great tools out there that make the entire process a lot easier. Check out https://regexr.com/ .

Create the span from matches

If there is a match in the string, then I need to wrap that text in a <span> and add the correct colour to the style. For the sake of clarity and cleaner code, this was written as its own function.

const createSpanFromMatches = (matches, text, restProps={}) => {
const content = text.split(`${matches[0]}[${matches[2]}]`)
return [...new Set(content)] // get the unique values / avoid ["", ""] - when there are no other parts of text
.map(text => text === "" ? // map over the unique values to replace that which was split
<span {...restProps} style={{color: `${matches[2]}`}} >{matches[1]}</span> // return the element with the colour
: text) // or return the text
}

I split the text using the matches, effectively extracting the central part of the string that has the text to be coloured and the information used to colour it. The content array then has the first part of the string, an empty string and the last part of the string.

If the original string looked like this: “We have a (new sale)[#f5f5f5] on!”. Then the split array will look like the following: ["We have a ", "", " on!"].

Using ...new Set(content) allows me to remove the duplicate values and avoid having two empty strings in the array. This happened when the entire string was coloured and there wasn't any remaining string before or after.

Then it is just a matter of mapping over the content returned from the split array and returning the correct <span> element. This will be processed normally by React and have the right style applied.

Wrapping the children in the function

The last step is to wrap the children shown in the Contentful options in the addColour function. Conveniently, I can add or remove the colour option from any component I want. However, inconveniently there is not a quick and easy way to add it to all components or content types in one fell swoop.

const options = {
renderMark: {
[MARKS.BOLD]: text => <span className="font-bold">{text}</span>
},
renderNode: {
[BLOCKS.HEADING_1]: (node, children) => <h2>{addColour(children)}</h2>,
[BLOCKS.PARAGRAPH]: (node, children) => <p>{addColour(children)}</p>
}
}

An example of it being implemented

Last thoughts

This was a quick and easy way to add colour to Contentful’s Rich Text. If you don’t use React, you can quickly adapt these functions to fit the framework of your choice.

As an avid Tailwind user , this implementation makes me think of another easy to add a feature that would allow the inclusion of classes to the Rich Text. You could use the same function to apply styling classes to the content depending on what you needed to do. And with Tailwinds easy to remember class names you have quite a lot of flexibility.

This article was posted on my blog a few weeks before here. If you want up to date articles like this. Feel free to check it out.

I design, build and implement technology focused solutions for client. Find out more at samloyd.io. If you need help or assistance building a product or service — lets talk.

You can also find me:

🐦 On Twitter @loyds_
📸 On Unsplash
📱 On Instagram @mr_samloyd

Entrepreneur. Developer. Designer. And occassionally interesting. Find out more at samloyd.io

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store