Behind the scenes of the Contentful rich text field

A hand interacting with a Contentful rich text fields
Published
June 26, 2020
Category

Developers

Everything you need to know about how rich text fields work in Contentful — how to configure them, how authors use them and how Contentful stores their content and style.

When to use a rich text field

Rich text fields give authors the ability to style content. For example, formatting headings and links. You can read more about when to use a rich text field and when to use Markdown in A beginner’s guide to Contentful text types: Markdown and Rich Text Format.

Rich text field capabilities

Rich text fields allow authors to use the following styles: 

  • Headings: H1, H2, H3, H4, H5, H6

  • Text formatting: Bold (B), Italic (I), Underline (U), Code Style

  • Lists: unordered lists, ordered lists

  • Links: URLs, assets, entries

  • Embeds: content entries (blocked and inline), assets

  • Other: blockquote, Horizontal Rule (HR)

Contentful gives administrators the ability to decide which styles authors can use within rich text fields. For example, you may not want authors to use ordered lists and disable that option. 

A screenshot of the Contentful rich text field settings tab

You can also tell Contentful exactly how you want to validate the field, including:

  • Making it required.

  • Limit how many characters they can enter.

  • Limit the number of entries they can link to or embed.

  • Limit the type of entries they can link to or embed.

  • Limit the number of assets they can link to or embed.

Screenshot of the Contentful rich text field validations tab

You can also add help text to guide your authors on what is acceptable content for that particular rich text field.

Screenshot of the Contentful Rich Text Fields Appearance Tab

Content stays separated from style

One of the main concerns I had with the rich text field editor was mixing HTML styling tags (e.g. <strong></strong>, <em></em>, etc.) with content. 

I'm used to rich text fields from CMSes that store HTML and content together (e.g., Here is a <strong>bolded word</strong>.) Image pulling that content for delivery through a voice control device. You'd have to write some code to strip out the HTML.

But Contentful handles this mix of content and style in a better way. They break apart the entire content inside the rich text field into a JSON object with all the styles — not as HTML, but as "nodeTypes" and "marks."

How Contentful stores rich text fields content 

Let's look at how this content is stored inside of Contentful.

Screenshot of Contentful Rich Text Field Sample Content

This rich text field entry has one (H1) heading, three bolded words, one italicized word, one link and one embedded inline entry. Originally, I expected this code would look something like this:

<h1>Contentful Rich Text Fields Heading</h1>
In this short blog article, I'll tell you all about 
<strong>Rich Text Fields</strong> in <em>Contentful</em>.
</br></br>
You'll learn how to <a href="http://www.mylink.com">create</a> them.
</br></br>
Here is another article you may be interested in:
</br></br>
[id:1232XXXDD3324]

This code works for a website, but not for a mobile app or voice control device. 

After publishing the article and pulling the content stored inside of Contentful using the CDA (Contentful Delivery API), I was able to see how Contentful breaks apart the content and stores the style in separate key/value pairs (e.g. "marks" or "nodeType").

{
    "sys": {
      "space": {
        "sys": {
          "type": "Link",
          "linkType": "Space",
          "id": "*******"
        }
      },
      "id": "*********",
      "type": "Entry",
      "createdAt": "2020-03-06T15:07:14.806Z",
      "updatedAt": "2020-03-06T15:47:57.562Z",
      "environment": {
        "sys": {
          "id": "*****",
          "type": "Link",
          "linkType": "Environment"
        }
      },
      "revision": 5,
      "contentType": {
        "sys": {
          "type": "Link",
          "linkType": "ContentType",
          "id": "test"
        }
      },
      "locale": "en-US"
    },
    "fields": {
        "richTextField": {
        "data": {},
        "content": [
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "Contentful Rich Text Fields Heading",
                "nodeType": "text"
              }
            ],
            "nodeType": "heading-1"
          },
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "In this short blog article, I'll tell you all about ",
                "nodeType": "text"
              },
              {
                "data": {},
                "marks": [
                  {
                    "type": "bold"
                  }
                ],
                "value": "Rich Text Fields",
                "nodeType": "text"
              },
              {
                "data": {},
                "marks": [],
                "value": " in ",
                "nodeType": "text"
              },
              {
                "data": {},
                "marks": [
                  {
                    "type": "italic"
                  }
                ],
                "value": "Contentful",
                "nodeType": "text"
              },
              {
                "data": {},
                "marks": [],
                "value": ".",
                "nodeType": "text"
              }
            ],
            "nodeType": "paragraph"
          },
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "You will learn how to ",
                "nodeType": "text"
              },
              {
                "data": {
                  "uri": "https://www.mylink.com"
                },
                "content": [
                  {
                    "data": {},
                    "marks": [],
                    "value": "create",
                    "nodeType": "text"
                  }
                ],
                "nodeType": "hyperlink"
              },
              {
                "data": {},
                "marks": [],
                "value": " them.",
                "nodeType": "text"
              }
            ],
            "nodeType": "paragraph"
          },
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "Here is another article you may be interested in:",
                "nodeType": "text"
              }
            ],
            "nodeType": "paragraph"
          },
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "",
                "nodeType": "text"
              },
              {
                "data": {
                  "target": {
                    "sys": {
                      "id": "****************",
                      "type": "Link",
                      "linkType": "Entry"
                    }
                  }
                },
                "content": [],
                "nodeType": "embedded-entry-inline"
              },
              {
                "data": {},
                "marks": [],
                "value": "",
                "nodeType": "text"
              }
            ],
            "nodeType": "paragraph"
          },
          {
            "data": {},
            "content": [
              {
                "data": {},
                "marks": [],
                "value": "",
                "nodeType": "text"
              }
            ],
            "nodeType": "paragraph"
          }
        ],
        "nodeType": "document"
      }
    }
  }

Developers can take this code and pair up the content with the appropriate style sheet. For example, our style sheets for each of our delivery channels can define the following rules:

  • Any nodeType of Paragraph should be formatted with a black background on the website, a transparent background on iOS app and left alone for Alexa.

  • Any nodeType of Heading-1 should be of color green, with a font size of 20pt and a padding around it of 10px, but only on the website. For the iOS app display it in white, with a black background and default font size. For Alexa, do nothing extra.

  • Any marks of type Italic, should use the CSS italic for the website, should be red for the iOS app and should be "emphasized" by the Alexa voice.

I'm not a designer, and content would appear super ugly with this style sheet. But the point is Contentful gives us nice clean content, without styles. Then it's up to us to format those styles however we want, depending on the delivery channel.

Happy creating! 

Thanks for taking the time to read this blog article. You can learn more through my Contentful { Creators } Podcast, watch my Contentful { Creators } On-Demand Webinars or join my Contentful { Creators } Meetup if you’re ever in Southern California.

About the author
Don't miss the latest
Get updates in your inbox
A monthly newsletter to help you build better digital experiences with Contentful.
add-circle remove style-two-pin-marker subtract-circle