Enriching rich text with inline components (DatoCMS + React)

Designers challenge us to come up with technical solutions to build their creations. With inline components we have a new tool to bring their imaginations to live. Here’s how we use DatoCMS structured text in combination with React.

The design challenge

A design agency recently challenged us with a nifty design for a new website. The design included a section with text and an image side-by-side. Normally this would be a straightforward section to build and create a content model for. However the text contains headings with inline icons and an external link that looks like a button at the end of a paragraph:

Example of design with inline icon component
Design with inline icon and external link components

So how do we enable content editors to craft this text with inline elements and how can we render all of this in the UI?

The CMS solution

Up until recently this challenge would have probably required us to model a form in the CMS where editors needed to construct this section with separate fields for each element. But now more and more (Headless) CMS’es have given super powers to rich text fields, allowing content editors to add custom elements directly inline:

This new grade of rich text is popularised by Sanity (portable text), now also available in other CMS’es like Contentful (rich text with embeds) and in this case using DatoCMS structured text. We configure our section text to allow inline “External Link” and “Icon” records:

Screenshots of settings in Dato CMS
Configuring structured text field in DatoCMS

The front-end code solution

We can now query the DatoCMS GraphQL API to obtain the rich text including the inlined components:

# components/TextImageSection/TextImageSection.fragment.graphql
fragment textImageSectionFragment on TextImageSectionRecord {
  text {
    links {
      ... on ExternalLinkRecord { _modelApiKey, id, title, url }
      ... on IconRecord { _modelApiKey, id, name, alignment }
  image { ... }

The result is a large blob of JSON representing the the rich text and all linked records. Luckily DatoCMS also provides a react-datocms package to easily render this. We configure the provided <StructuredText> component to render the inline records with our own custom <Icon> and <ExternalLink>:

// components/TextImageSection/TextImageSection.jsx
import { Image, StructuredText } from 'react-datocms'
import { Icon, ExternalLink } from './elsewhere'

 * @param {import('../../lib/dato').TextImageSectionRecord} props 
export default function TextImageSection({ id, text, image }) {
  return (
        data={ text }
        renderInlineRecord={ InlineRecord }
      <DatoImage data={ image.responsiveImage } />

function InlineRecord ({ record }) {
  switch (record._modelApiKey) {
    case 'icon':
      return (
          name={ }
          class={ `icon--${record.alignment}` }
    case 'external_link':
      return (
          title={ record.title }
          url={ record.url }
          class={ 'external-link' }
      console.warn(`No inline record specified for "${ record._modelApiKey }"`)
      return null

The result, rich text with inline components (view live on website):

Example of design with inline icon component

Enjoy the enhanced content experience!

ps. noticed the use of GraphQL fragments and content types in our code? You can read all about it in our blog posts Componentize data with GraphQL fragments and CMS-driven IntelliSense in your code editor.

← All blog posts