Custom Decap CMS previews in Gatsby

You’re using Decap CMS1 to manage the content of your Gatsby-powered website. The default Decap CMS preview displays every field, including metadata. That probably isn’t what you want.

Register a preview template

The Gatsby Decap CMS plugin allows you to customise Decap CMS using a JavaScript module. In the example below, we tell Gatsby to use our decap.js module.

// ./gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: 'gatsby-plugin-decap-cms',
      options: {
        modulePath: `${__dirname}/src/cms/decap.js`,
      },
    },
  ],
}

Our module defines a preview template for the “articles” collection and registers it with Decap CMS.

// ./src/cms/decap.js
import CMS from 'decap-cms-app'
import React from 'react'

const ArticlePreview = () => (
  <article>
    <h1>Custom article preview</h1>
    <p>This is my new article.</p>
  </article>
)

CMS.registerPreviewTemplate('articles', ArticlePreview)

Don’t repeat yourself

In reality, we probably already have an “article” component we can use. Here’s a basic example which accepts the article title and body.

// ./src/components/article.js
import React from "react"

export default function Article({ title, children }) (
  <article>
    <h1>{title}</h1>
    {children}
  </article>
)

Here is our updated preview module.

import CMS from 'decap-cms-app'
import React from 'react'
import Article from '../components/articles/article'

const ArticlePreview = () => (
  <Article title="Custom article preview">
    <p>This is my new article.</p>
  </Article>
)

CMS.registerPreviewTemplate('articles', ArticlePreview)

Populate the preview template

The above example works, but it doesn’t do anything of value. For that, we need to retrieve the article content.

Decap CMS passes a PreviewTemplateComponentProps object to our preview component. We’re interested in the entry and widgetFor properties.

The entry property is an immutable map containing a data key. The data value is an immutable map containing our article fields.

Set the article title

The article title is a plain string, so we pass it directly to the Article component.

const ArticlePreview = ({ entry }) => (
  <Article title={entry.getIn(['data', 'title'])}>
    <p>This is my new article.</p>
  </Article>
)

Set the article body

The article body uses a Markdown widget. We need to convert the raw Markdown into HTML. That’s where the widgetFor property comes into play.

const ArticlePreview = ({ entry, widgetFor }) => (
  <Article title={entry.getIn(['data', 'title'])}>
    <section dangerouslySetInnerHTML={{ __html: widgetFor('body') }} />
  </Article>
)

Footnotes

  1. This blog post originally referred to Netlify CMS, which was rebranded as Decap CMS in early 2023. I’ve updated the product name and documentation links, but the content is otherwise unchanged.

Sign up for my newsletter

A monthly round-up of blog posts, projects, and internet oddments.