Was this page helpful?

What is rich text?

Rich Text is a field type that enables authors to create rich text content, similar to traditional "What you see is what you get" (wysiwyg) editors. The key difference here is that the Contentful Rich Text field response is returned as pure JSON rather than HTML. It offers common text formatting options such as paragraphs, lists and blockquotes, and allows entries and assets within our Contentful space to be linked dynamically and embedded within the flow of the text.

Rich Text on the Web App

An animated GIF of scrolling through a Rich Text editor field with lots of different types of rich content

The menu bar at the top of the Rich Text editor provides authors with all the usual text formatting capabilities, including creating links to a static URL, and inserting links to Contentful entries and assets from within the same Contentful space.

The Embed Entry button (top right) embeds a Contentful entry as a block or inline element.

Customization

A key aspect of Rich Text is the possibility to customize the field in a way that authors are limited to using specified formatting options.

For example, you can limit the formatting options to only include paragraph tags, or limit the kinds of entries that can be hyperlinked or embedded.

A screenshot of the Rich Text editor, showing the differences in the top toolbar with formatting options adjusted

Customization can be done on the Web App or through the API.

Rich Text on the API Response

The Rich Text field API response is returned as a JSON array of nodes that follows the format of an abstract syntax tree.

The following is an example of a Rich Text field REST API response which returns a paragraph — "This text is important" — with the word "important" marked as bold:

{
  "nodeType": "document",
  "data": {},
  "content": [
    {
      "nodeType": "paragraph", // Can be paragraphs, images, lists, embedded entries
      "data": {},
      "content": [
        {
          "nodeType": "text",
          "value": "This text is ",
          "data": {},
          "marks": []
        },
        {
          "nodeType": "text",
          "value": "important",
          "data": {},
          "marks": [
            "type": "bold" // Can be bold, underline, italicss
          ]
        }
      ]
    }
  ]
}

Embedded and Linked Entries in Rich Text

Rich Text allows editors to link and embed entries in the flow of text in the UI. These links are returned in the Rich Text field API response as references, and the referenced data is returned in a separate object (more on this below). As a developer, this gives you the flexibility on the front end to build out the HTML you need for linked assets and entries rather than having to deal with opinionated HTML and formatting from the API.

For example, you might want to:

  • Use custom anchor link wrappers, such as a React Router link or a NextJS Link in your single page application for inline links in Rich Text
  • Use the Contentful Images API to resize, crop and manipulate an image that is returned as a linked asset
  • Render a widget such as an image gallery, a product description box, a sign up form, an annotation window or anything else in the flow of the Rich Text field!

The following screenshot shows a hyperlinked entry, followed by an embedded entry.

Screenshot of an embedded hyperlink and embedded entry in the Rich Text field editor

This is an example of an API response for a hyperlinked entry:

{
  data: {
    target: {
      sys: {
        id: "12345", // Linked entry ID
        type: "Link",
        linkType: "Entry"  // This is a linked entry — observe this is a reference with no data — yet!
      }
    }
  },
  content: [
    {
      marks: [],
      value: "the link text",
      nodeType: "text"
    }
  ],
  nodeType: "entry-hyperlink" # The nodeType tells us how the editor linked the entry
}

and this is an example of an API response for an embedded entry:

{
  data: {
    target: {
      sys: {
        id: "67890", # Linked entry ID
        type: "Link",
        linkType: "Entry" # This is a linked entry — observe this is a reference with no data — yet!
      }
    }
  },
  content: [
    {
      marks: [ ],
      value: "",
      nodeType: "text"
    }
  ],
    nodeType: "embedded-entry-inline" # The nodeType tells us how the editor linked the entry
}

Rendering Rich Text references using the REST API

Observe above that the linked entries in the flow of the Rich Text field are returned as references with no accompanying data. The data for the linked entries referenced in the flow of the Rich Text field are in a separate node that is returned from the REST API response: the includes object.

  • includes.Entry contains the data for the linked entries referenced in the Rich Text field response
  • includes.Asset includes the data for the linked assets referenced in the Rich Text field response (e.g. images and media)

Here's an example of the includes node in the REST API response, alongside the items node that contains the requested fields Rich Text field response, referencing the linked entries.

{
  "items": [
    {
      "fields": {
        "richText": {
          "content": [
            {
              "data": {
                "target": {
                  "sys": {
                    "id": "12345", // Find the entry with this ID in the includes["Entry"] object below
                    "type": "Link",
                    "linkType": "Entry"
                  }
                }
              },
              "content": [],
              "nodeType": "embedded-entry-block"
            },
            {
              "data": {
                "target": {
                  "sys": {
                    "id": "67890", // Find the entry with this ID in the includes["Asset"] object below
                    "type": "Link",
                    "linkType": "Asset"
                  }
                }
              },
              "content": [],
              "nodeType": "embedded-asset-block"
            }
          ]
        }
      }
    }
  ],
  "includes": {
    "Entry": [
      {
        "sys": {
          "id": "12345", // Here we have all the data for the linked Entry inside the Rich Text field response
          "type": "Entry",
          "contentType": {
            "sys": {
              "type": "Link",
              "linkType": "ContentType",
              "id": "videoEmbed"
            }
          }
        },
        "fields": {
          "title": "Example video embed",
          "embedUrl": "https://www.youtube.com/embed/12345"
        }
      }
    ],
    "Asset": [
      {
        "sys": {
          "id": "67890", // Here we have all the data for the linked Asset inside the Rich Text field response
          "type": "Asset"
        },
        "fields": {
          "title": "my-image",
          "description": "Image description",
          "file": {
            "url": "//images.ctfassets.net/.../example.jpg",
            "fileName": "example.jpg"
          }
        }
      }
    ]
  }
}

If you're using a Contentful client library to make a call to the Contentful REST API, those linked assets and entries in the Rich Text field will be resolved for you. This means that the data will be inserted into the flow of the Rich Text field, allowing you to access e.g. richText.content[0].data.target.fields.file.url as expected in the example above.

To understand how the client libraries resolve links in the Rich Text field, take a look at the contentful-resolve-response package, which converts the flat nodes into a rich tree of data when using the JavaScript client library.

To sum up:

  • Hyperlinking an entry means to render an anchor link that points towards the URL of a separate entry
  • Embedding an entry means to render the contents of this linked Entry in the flow of text, whether on an inline or a block level
  • References to linked entries and assets are provided in the raw Rich Text response from the REST API
  • References to linked entries and assets are resolved for you if you're using a Contentful client library to make the request to Contentful

Rendering the Rich Text response from the REST API with linked assets and entries on the front end

Contentful provides you with tools to speed up your workflow on the front end and to allow you to work with the Rich Text field data and render the nodes into HTML — Rich Text field renderers. There are a number of Rich Text field renderer packages available for your favorite programming languages and frameworks — view on GitHub.

The following code example uses a response from the REST API processed by the JavaScript client library, and renders it using the Contentful Rich Text React Renderer available from NPM to demonstrate the concepts in JavaScript and React.

The Rich Text field response below includes a linked entry of type videoEmbed and a linked image asset.

import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS } from '@contentful/rich-text-types';

// Create a bespoke renderOptions object to target BLOCKS.EMBEDDED_ENTRY (linked entries e.g. videoEmbed
// and BLOCKS.EMBEDDED_ASSET (linked assets e.g. images)

const renderOptions = {
  renderNode: {
    [BLOCKS.EMBEDDED_ENTRY]: (node, children) => {
      if (node.data.target.sys.contentType.sys.id === 'videoEmbed') {
        return (
          <iframe
            src={node.data.target.fields.embedUrl}
            height="100%"
            width="100%"
            frameBorder="0"
            scrolling="no"
            title={node.data.target.fields.title}
            allowFullScreen={true}
          />
        );
      }
    },

    [BLOCKS.EMBEDDED_ASSET]: (node, children) => {
      // render the EMBEDDED_ASSET as you need
      return (
        <img
          src={`https://${node.data.target.fields.file.url}`}
          height={node.data.target.fields.file.details.image.height}
          width={node.data.target.fields.file.details.image.width}
          alt={node.data.target.fields.description}
        />
      );
    },
  },
};

export default function RichTextResponse({ richTextResponse }) {
  return <>{documentToReactComponents(richTextResponse, renderOptions)}</>;
}

Rendering the Rich Text response from the GraphQL API with linked assets and entries on the front end

The Contentful GraphQL API doesn’t require an client library to handle linked entries.

The Rich Text field response from the GraphQL API is different and contains two top-level nodes.

"richTextResponse": {
  // JSON structure of the Rich Text field
  "json": {
    # ...
  }
  // all referenced assets/entries
  "links": {
    # ...
  }
}

The references are not automatically resolved inside of the Rich Text JSON from the GraphQL API. This means we have to take a different approach to render and resolve links when using GraphQL.

You can still use documentToReactComponents from the rich-text-react-render: to render Rich Text field data to the DOM, but instead of passing in an options object, you'll need to construct the object using a custom function to process some logic to resolve the links.

A common solution to rendering the embedded asset inline with the Rich Text response in JavaScript is to create a map of the assets (id: asset), and find the asset to display in the map when rendering an asset block.

import { documentToReactComponents } from '@contentful/rich-text-react-renderer';
import { BLOCKS } from '@contentful/rich-text-types';

// Create a bespoke renderOptions object to target BLOCKS.EMBEDDED_ENTRY (linked entries e.g. videoEmbed)
// and BLOCKS.EMBEDDED_ASSET (linked assets e.g. images)

function renderOptions(links) {
  // create an asset block map
  const assetBlockMap = new Map();
  // loop through the assets and add them to the map
  for (const asset of links.assets.block) {
    assetBlockMap.set(asset.sys.id, asset);
  }

  // create an entry block map
  const entryBlockMap = new Map();
  // loop through the assets and add them to the map
  for (const entry of links.entries.block) {
    entryBlockMap.set(entry.sys.id, entry);
  }

  return {
    // other options...

    renderNode: {
      // other options...

      [BLOCKS.EMBEDDED_ENTRY]: (node, children) => {
        // find the entry in the entryBlockMap by ID
        const entry = entryBlockMap.get(node.data.target.sys.id);

        // render the entries as needed by looking at the __typename
        // referenced in the GraphQL query

        if (entry.__typename === 'VideoEmbed') {
          return (
            <iframe
              src={entry.embedUrl}
              height="100%"
              width="100%"
              frameBorder="0"
              scrolling="no"
              title={entry.title}
              allowFullScreen={true}
            />
          );
        }
      },
      [BLOCKS.EMBEDDED_ASSET]: (node, next) => {
        // find the asset in the assetBlockMap by ID
        const asset = assetBlockMap.get(node.data.target.sys.id);

        // render the asset accordingly
        return <img src={asset.url} alt="My image alt text" />;
      },
    },
  };
}

// Render richTextResponse.json to the DOM using
// documentToReactComponents from "@contentful/rich-text-react-renderer"

export default function RichTextResponse({ richTextResponse }) {
  return (
    <>
      {documentToReactComponents(
        richTextResponse.json,
        renderOptions(richTextResponse.links)
      )}
    </>
  );
}

Rich Text Commands

Rich Text Commands is a power-user feature that increases the efficiency of editors working with structured content in the Rich Text editor. This feature enables the user embed assets and entries using a command panel without leaving the editor context or keyboard. The command panel opens as soon as the user types a slash (/).

Rules of Rich Text

  • The document must have only one root node with nodeType: 'document'.
  • Nested Documents are not allowed.
  • Nodes in the document can be of type Block, Inline or Text, see: type definitions, list of supported blocks, inlines.
  • Document can contain only nodes type of Block as direct children.
  • Blocks can contain nodes of type Block, Inline, or Text.
  • Nested Blocks are whitelisted, see the list of containers.
  • Void nodes are whitelisted, the list of void nodes.
  • Inlines can contain nodes of type Inline or Text.
  • Text can have a list of Marks associated with it, see list of supported marks.
  • Sibling Text nodes with equal marks should be grouped.
  • Custom node types, data properties and marks are not allowed.

Next steps

Not what you’re looking for? Try our FAQ.