Rendering CMS Rich Text Safely in Gatsby and React

Hero image for Rendering CMS Rich Text Safely in Gatsby and React. Image by Nick van den Berg.
Hero image for 'Rendering CMS Rich Text Safely in Gatsby and React.' Image by Nick van den Berg.

In Brief

CMS rich text should be rendered as structured content, not injected as trusted HTML. Map known nodes to React components, handle links and embeds deliberately, validate unsupported content shapes and keep fallbacks safe when editors create content the frontend did not expect.

CMS rich text looks harmless until real editors start using it.

A few paragraphs are easy. Production content brings embedded entries, assets, links, headings, lists, editorcreated structure and content shapes nobody tested in the first implementation. Gatsby and React then have to turn that CMS structure into safe, accessible and predictable HTML.

The mistake to avoid is treating rich text as a blob of trusted HTML. That shortcut may feel quicker, but it gives away the main benefit of structured rich text: the frontend can decide exactly what each node is allowed to become.


Rich Text is Structured Data

In Contentful and similar CMSes, rich text is usually a document tree. It represents paragraphs, headings, lists, links, marks, assets and embedded entries.

The Contentful rich text documentation explains the structure. The Gatsby or React job is to map that structure onto project components without making the renderer too trusting.

That mapping needs to be deliberate.


Map Nodes to Components Explicitly

Do not hide rich text rendering inside a oneline utility and forget about it.

Decide how the renderer handles:

  • paragraphs
  • headings
  • unordered lists
  • ordered lists
  • internal links
  • external links
  • embedded assets
  • embedded entries
  • quotes
  • code blocks
  • unsupported nodes

The point is not ceremony. It is control. A paragraph should render as a paragraph. A link should have meaningful text and the right attributes. An embedded article card, video or code snippet should become the component the site expects, not a loose fragment of markup.


Keep Heading Levels Under Control

CMS rich text can create poor document structure if editors can choose any heading level anywhere.

An article title may already be the page h1. Body rich text should usually start below that level. If an editor adds another h1 inside the content body, the rendered page becomes harder for people, crawlers and assistive technology to understand.

The renderer can normalise heading levels, but do not make it silently repair every editorial mistake. The CMS model, editorial guidance and frontend renderer need to agree on what the page structure is allowed to do.


Rich text links are not all the same.

You may need to handle:

  • ordinary external URLs
  • internal entry links
  • asset links
  • email links
  • anchor links
  • broken or unpublished references

For internal entries, resolve the entry to a real site URL. Do not leak CMS IDs into the page. For external links, decide how target, rel and visible link text should work.

A missing linked entry should fail clearly during build, preview or rendering. It should not produce an empty anchor, a broken card or a link that looks clickable but goes nowhere.


Treat Embedded Entries as Real Components

Embedded entries are where rich text becomes operationally interesting.

They might represent code snippets, callouts, product cards, related articles, forms, videos or image blocks. Each one has its own required fields and failure modes.

The article on rendering Contentful Rich Code snippets in Gatsby is one example. A code snippet entry needs syntax, language, caption and safe markup. It should not be treated like an ordinary paragraph.

The same thinking applies to content architecture more broadly. Planning content models for a headless CMS covers the modelling side, while headless CMS integration covers the implementation work needed to make publishing, preview and rendering dependable.


Avoid Unsafe HTML Shortcuts

React makes it possible to inject HTML directly. Sometimes legacy content leaves no easy alternative, but it should be a conscious decision with sanitisation, allowed tags and editor permissions understood.

If the CMS already gives you structured rich text, render known nodes to known React components. That keeps control in the application, where it can be tested.

The opposite mistake is possible too. Oversanitising can remove legitimate editorial content, break embeds, strip useful link attributes or damage accessibility. A safe renderer should be strict about unknown content without flattening the content editors actually need.


Test Awkward Content, Not the Neat Demo

The useful test cases are rarely the clean ones.

Test rich text with:

  • nested lists
  • empty paragraphs
  • long link text
  • internal entry links
  • missing linked entries
  • unpublished references
  • embedded entries inside awkward positions
  • missing asset metadata
  • headings at the wrong level
  • unsupported node types
  • image alt text
  • bold and italic marks mixed with links

This is where production constraints show up. A renderer that works for three neat paragraphs has not really been tested.


Build Failure‑Safe Rendering

The renderer should make bad content visible without breaking the whole page where avoidable.

For a missing optional embed, a clear fallback may be enough. For a required linked entry, failing the build or preview may be better because a silent failure would publish misleading content. The right behaviour depends on the content type and page purpose.

For SEO and editorial control, this also connects to the headless CMS SEO controls matrix. Rich text is not only typography. It can affect headings, internal links, image context, structured content and the reliability of rendered pages.


The Practical Rule

Render CMS rich text as a contract between the content model and the frontend.

Map known nodes deliberately. Validate the shapes editors can create. Handle links and embeds with care. Test the awkward cases. Keep fallbacks safe. That gives editors useful flexibility without making the rendered page unpredictable.


Planning a platform change?

I help teams make difficult platform work clearer, from architecture decisions and migrations to launch recovery, performance, and search visibility.