Custom Netlify CMS previews in Gatsby

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

Register a preview template

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

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

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

// ./src/cms/netlify.js
import CMS from 'netlify-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>
)

And here is our updated preview module.

import CMS from 'netlify-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.

Netlify 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>
)

Sign up for my newsletter

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