Working with the GraphCMS Rich Text field

In this post, we'll look at the Rich Text field. We'll take a peek at how you can query, and mutate Rich Text using...

Jamie Barton
Jamie Barton
Working with the Rich Text Field

GraphCMS boasts an impressive collection of Field Types that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom Asset, Location, JSON, and, RichText scalars.

In this post we'll look at the Rich Text field. We'll take a peak at how you can query, and mutate Rich Text using the Content API.

graphcms-content-editor.png

When editing content, you'll be presented with a text editor that gives you the ability to format paragraphs, headings, lists, and embed iframes, tables, and assets.

When you save content, it is saved as an abstract syntax tree (AST), and can be queried in the format of HTML, Markdown, Text, and the "raw" AST itself (JSON). The AST is based on Slate.

If you open the API Playground documentation, you'll be able to search for the RichText type.

You'll see it returns its own GraphQL type:

type RichText {
raw: RichTextAST!
html: String!
markdown String!
text: String!
}

How you use the fields for RichText will depend on your application. Let's take a look at the different methods of fetching, displaying, and mutating rich text below.

Querying Rich TextAnchor

Just like any other field in your content model, you can fetch your rich text content, but in any (or all) of the formats typed above.

For example, let's fetch the raw, html, markdown, and text values for the custom field content which is of type RichText:

{
articles {
content {
raw
html
markdown
text
}
}
}

This query will return the following:

{
"data": {
"articles": [
{
"content": {
"raw": {
"children": [
{
"type": "paragraph",
"children": [
{
"text": "GraphQL CMS"
}
]
}
]
},
"html": "<p>GraphQL CMS</p>",
"markdown": "GraphQL CMS\n",
"text": "GraphQL CMS"
}
}
]
}
}

For the purposes of this article, I'm just returning a single paragraph, but for each of the different nodes you add to the rich text editor, it will return a "node" with type, and the children.

Slate has different node types, they are:

  • A root-level Editor node that contains the entire document's content
  • Container Element nodes which have semantic meaning in your domain.
  • And leaf-level Text nodes which contain the document's text.

These combined are shown above in the query response. Learn more about Slate nodes.

Rendering Rich TextAnchor

Depending on which of the RichText types you are querying, your presentation implementation will vary.

rawAnchor

When using the raw AST (Slate nodes) you will have ultimate control over how these nodes are presented to the user.

Let's have a look at a more complex AST response for the first two paragraphs of this article:

{
"data": {
"articles": [
{
"content": {
"raw": {
"children": [
{
"type": "paragraph",
"children": [
{
"text": "GraphCMS boasts an impressive collection of "
},
{
"href": "https://graphcms.com/docs/schema/field-types",
"type": "link",
"children": [
{
"text": "Field Types"
}
]
},
{
"text": " that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom "
},
{
"href": "https://graphcms.com/docs/schema/field-types#asset",
"type": "link",
"children": [
{
"text": "Asset"
}
]
},
{
"text": ", "
},
{
"href": "https://graphcms.com/docs/schema/field-types#location",
"type": "link",
"children": [
{
"text": "Location"
}
]
},
{
"text": ", "
},
{
"href": "https://graphcms.com/docs/schema/field-types#json",
"type": "link",
"children": [
{
"text": "JSON"
}
]
},
{
"text": ", and, "
},
{
"href": "https://graphcms.com/docs/schema/field-types#rich-text",
"type": "link",
"children": [
{
"text": "RichText"
}
]
},
{
"text": " scalars."
}
]
},
{
"type": "paragraph",
"children": [
{
"text": ""
}
]
},
{
"type": "paragraph",
"children": [
{
"text": "In this post we'll look at the Rich Text field. We'll take a peak at how you can query, and mutate Rich Text using the Content API."
}
]
},
{
"type": "paragraph",
"children": [
{
"text": ""
}
]
},
{
"src": "https://media.graphcms.com/N3JOKsXrT9ezCU4Ba6LI",
"type": "image",
"title": "Screenshot 2021-03-24 at 13.00.14.png",
"width": 2408,
"handle": "N3JOKsXrT9ezCU4Ba6LI",
"height": 1684,
"children": [
{
"text": ""
}
],
"mimeType": "image/png"
},
{
"type": "paragraph",
"children": [
{
"text": ""
}
]
}
]
}
}
}
]
}
}

As you can see, we have an array of different node types. It's up to you to display this in your application.

You may begin by having a dictionary (or Map) of your node type renderers as key/value pairs. Slate have an example of how you can use a switch statement in JavaScript to return different tags based on the type.

const renderElement = useCallback(({ attributes, children, element }) => {
switch (element.type) {
case 'quote':
return <blockquote {...attributes}>{children}</blockquote>
case 'link':
return (
<a {...attributes} href={element.url}>
{children}
</a>
)
default:
return <p {...attributes}>{children}</p>
}
}, [])

htmlAnchor

Rendering the HTML of your rich text is quite simple, but comes without much customization. GraphCMS will automatically parse the Slate raw output into HTML elements you can pass straight to the DOM.

Using the query above to fetch articles, let's now fetch just the html.

{
"data": {
"articles": [
{
"content": {
"html": "<p>GraphCMS boasts an impressive collection of <a title=\"https://graphcms.com/docs/schema/field-types\" href=\"https://graphcms.com/docs/schema/field-types\">Field Types</a> that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom <a title=\"https://graphcms.com/docs/schema/field-types#asset\" href=\"https://graphcms.com/docs/schema/field-types#asset\">Asset</a>, <a title=\"https://graphcms.com/docs/schema/field-types#location\" href=\"https://graphcms.com/docs/schema/field-types#location\">Location</a>, <a title=\"https://graphcms.com/docs/schema/field-types#json\" href=\"https://graphcms.com/docs/schema/field-types#json\">JSON</a>, and, <a title=\"https://graphcms.com/docs/schema/field-types#rich-text\" href=\"https://graphcms.com/docs/schema/field-types#rich-text\">RichText</a> scalars.</p><p></p><p>In this post we&#39;ll look at the Rich Text field. We&#39;ll take a peak at how you can query, and mutate Rich Text using the Content API.</p><p></p><img src=\"https://media.graphcms.com/N3JOKsXrT9ezCU4Ba6LI\" alt=\"Screenshot 2021-03-24 at 13.00.14.png\" title=\"Screenshot 2021-03-24 at 13.00.14.png\" width=\"2408\" height=\"1684\" /><p></p>"
}
}
]
}
}

You can then just output this HTML directly to the page.

If you are using React, it's a little different. However, you can safely (because you know the source of truth) use dangerouslySetInnerHTML for setting HTML.

For example:

const article = {
"content": {
"html": "<p>GraphCMS boasts an impressive collection of <a title=\"https://graphcms.com/docs/schema/field-types\" href=\"https://graphcms.com/docs/schema/field-types\">Field Types</a> that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom <a title=\"https://graphcms.com/docs/schema/field-types#asset\" href=\"https://graphcms.com/docs/schema/field-types#asset\">Asset</a>, <a title=\"https://graphcms.com/docs/schema/field-types#location\" href=\"https://graphcms.com/docs/schema/field-types#location\">Location</a>, <a title=\"https://graphcms.com/docs/schema/field-types#json\" href=\"https://graphcms.com/docs/schema/field-types#json\">JSON</a>, and, <a title=\"https://graphcms.com/docs/schema/field-types#rich-text\" href=\"https://graphcms.com/docs/schema/field-types#rich-text\">RichText</a> scalars.</p><p></p><p>In this post we&#39;ll look at the Rich Text field. We&#39;ll take a peak at how you can query, and mutate Rich Text using the Content API.</p><p></p><img src=\"https://media.graphcms.com/N3JOKsXrT9ezCU4Ba6LI\" alt=\"Screenshot 2021-03-24 at 13.00.14.png\" title=\"Screenshot 2021-03-24 at 13.00.14.png\" width=\"2408\" height=\"1684\" /><p></p>"
}
}
function MyComponent() {
return <div dangerouslySetInnerHTML={article.content.html} />;
}

markdownAnchor

To present markdown on a page, you'll need a markdown parser that will convert markdown to HTML. Here are a few that come to mind;

Depending on your frontend stack, there will most likely be a markdown renderer for you.

It's important to remember that some renderers will handle the parse and transformation client side, so depending on the length of content, this could increase affect your performance metrics.

If you can, compute the HTML ahead of time, and render only HTML. You might find using the html output more performant instead.

If you opt to use MDX, you can have even more customisability than you can the raw AST. You can even inject dynamic React components based on your Markdown node types.

textAnchor

There aren't many use cases for rendering just the text other than using it for a "excerpt". This is because any links, or images will be stripped out, and you'll need to sanitize any new lines and breaks yourself before rendering into a DOM element.

For the same Rich Text we created earlier (the first two paragraphs of this article) you'll be presented with the following from the GraphQL query:

GraphCMS boasts an impressive collection of \\nField Types\\n that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom \\nAsset\\n, \\nLocation\\n, \\nJSON\\n, and, \\nRichText\\n scalars.\\n\\nIn this post we'll look at the Rich Text field. We'll take a peak at how you can query, and mutate Rich Text using the Content API.\\n\\n\\n

Mutating Rich TextAnchor

Like every other content model, and field type inside GraphCMS, you can also mutate data using the Mutations API.

You'll typically mutate rich text using the Mutations API if you are accepting rich text submitted by a user in your own application, or if you have built your own content editor on top of GraphCMS.

However you're planning to mutate rich text, you will need to pass it to GraphCMS in the format of the Slate AST nodes we described above.

Here's an example of a Mutation:

mutation createArticle($title:String, $content:RichTextAST) {
createArticle(data: {
title: $title,
content: $content
}) {
title
content {
raw
}
}
}

... and the variables:

{
"title":"Working with the GraphCMS Rich Text field",
"content":{
"children":[
{
"type":"paragraph",
"children":[
{
"text":"GraphCMS boasts an impressive collection of "
},
{
"href":"https://graphcms.com/docs/schema/field-types",
"type":"link",
"children":[
{
"text":"Field Types"
}
]
},
{
"text":" that you can use when content modelling. These field types range from the core GraphQL scalar types, to custom "
},
{
"href":"https://graphcms.com/docs/schema/field-types#asset",
"type":"link",
"children":[
{
"text":"Asset"
}
]
},
{
"text":", "
},
{
"href":"https://graphcms.com/docs/schema/field-types#location",
"type":"link",
"children":[
{
"text":"Location"
}
]
},
{
"text":", "
},
{
"href":"https://graphcms.com/docs/schema/field-types#json",
"type":"link",
"children":[
{
"text":"JSON"
}
]
},
{
"text":", and, "
},
{
"href":"https://graphcms.com/docs/schema/field-types#rich-text",
"type":"link",
"children":[
{
"text":"RichText"
}
]
},
{
"text":" scalars."
}
]
},
{
"type":"paragraph",
"children":[
{
"text":""
}
]
},
{
"type":"paragraph",
"children":[
{
"text":"In this post we'll look at the Rich Text field. We'll take a peak at how you can query, and mutate Rich Text using the Content API."
}
]
},
{
"type":"paragraph",
"children":[
{
"text":""
}
]
},
{
"src":"https://media.graphcms.com/N3JOKsXrT9ezCU4Ba6LI",
"type":"image",
"title":"Screenshot 2021-03-24 at 13.00.14.png",
"width":2408,
"handle":"N3JOKsXrT9ezCU4Ba6LI",
"height":1684,
"children":[
{
"text":""
}
],
"mimeType":"image/png"
},
{
"type":"paragraph",
"children":[
{
"text":""
}
]
}
]
}
}

Slate provides a nice editor out of the box for React you can use, slate-react. See "Installing Slate" for more on this.

It's Easy To Get Started

GraphCMS plans are flexibly suited to accommodate your growth. Get started for free, or request a demo to discuss larger projects with more complex needs