Draft posts in GatsbyJs

11 Oct, 2022 (updated: 24 Oct, 2022)
752 words | 4 min to read | 2 hr, 15 min to write

This blog is written using gatsbyjs, a powerful static site generator.

I have a pile of articles that aren’t ready for publishing, and I thought of how can I hide them on prod, while making visible on local. And by “making visible” I mean it should be apparent that a post is in a non-published state so that I have a visual cue.

I thought I can perhaps use a different git branch, i.e. git checkout -b drafts and store drafts there and then cherry-pick posts that are ready to be published into main. But then it’s hard to see which articles aren’t ready yet as there won’t be any distinguisher on the front-end side. I.e. if I checkout drafts branch, all the posts will be blended together. That’d be a no-effort solution, but as it had this drawback that I could see would be annoying I decided to actually code it up.

Criteria (DOD)

For those who didn’t know, DOD stands for Definition Of Done

  • Being able to marks posts as draft
  • Draft posts must be visible on dev
  • On dev there must be a visual cue that the post is a draft
  • Draft posts must be hidden on prod

Let’s get into this!

draft field on frontmatter

Frontmatter is extra metadata in YAML defined for the markdown file and it’s separated from the main content of the markdown with triple dashes:

---
title: Draft posts in GatsbyJs
description: Make posts you are working on visible in local env and hidden on prod
date: 2022-10-11
tags:
- gatsby
---
This blog is written using [gatsbyjs](https://gatsbyjs.com/), a powerful static site generator.
...

So the frontmatter for the above markdown can be described as the following JSON:

{
"title": "Draft posts in GatsbyJs",
"description": "Make posts you are working on visible in local env and hidden on prod",
"date": "2022-10-11",
"tags": ["gatsby"]
}

Let’s define the draft field on the frontmatter:

---
title: Draft posts in GatsbyJs
description: Make posts you are working on visible in local env and hidden on prod
date: 2022-10-11
tags:
- gatsby
draft: true
---
This blog is written using [gatsbyjs](https://gatsbyjs.com/), a powerful static site generator.
...

Describe the type to be boolean in the gatsby-node.js using createSchemaCustomization:

exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
...
createTypes(`
...
type Frontmatter {
title: String
description: String
date: Date @dateformat
...
draft: Boolean
}
...
`)
}

Now, you can already use it as it is, but draft !== published because whether the article is published depends on environment. Drafts ARE published on local, but ARE NOT published on prod.

We can do just that hooking into graphql node creation using the onCreateNode API.

Adding published field on GraphQL node creation

As we just established, a post should be published if draft !== true OR environment is not prod.

As such, published field depends on the context.

  • published = true for all the posts (even those that are drafts) in non-prod context (or env)
  • published = false whenever a post is a draft in production context
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
...
createNodeField({
name: `published`,
node,
value: node.frontmatter.draft !== true || !isProd,
})
...
}
}

Determining whether the current env is prod or not can be done using process.env.NODE_ENV:

const process = require(`process`)
const isProd = process.env.NODE_ENV === 'production'

Using both post.frontmatter.draft and post.fields.published

All the content now can be filtered using a GraphQL filter

filter: {fields: {published: {eq: true}}}

which should be incorporated in all the queries where applicable. This is the pageQuery of this blog distilled only to the relative parts:

export const pageQuery = graphql`
query {
allMdx(
sort: { fields: [frontmatter___date], order: DESC },
filter: {fields: {published: {eq: true}}}
) {
fields {
slug
published
}
nodes {
frontmatter {
title
description
draft
}
}
}
}
`

And there you have it! But one little touch… As you remember I wanted visual cues for when a post is in a draft state.

Visual cues for drafts

I simply render a little “editing” sign next to any post title that is in draft state:

const isDraft = post.frontmatter.draft === true
... JSX:
<span itemProp="headline">
{isDraft && <DraftSign />}
{title}
</span>