Steven Mercatante

Steven Mercatante

Publish Posts After a Date in Gatsby

I like writing articles ahead of time and only have them listed on my site based on whether the current date is greater than or equal to the article's date attribute. This means I get to write a bunch of content at once, but release it over time so there's a constant stream of new material on my site without having to rush to write it the night before.

Note: This lesson builds upon my previous Add a Published Field to Your Gatsby Posts to Control Their Visibility article.

The Solution

First, make sure your article/post/whatever has a date attribute in its frontmatter. For example, here's this post's frontmatter:

---
title: Publish Posts Based After a Date in Gatsby
date: '2019-10-08'
published: true
---

I'm already filtering my query to check the article's published attribute, so I need to add an additional check against its date. Let's add a filter that only returns articles whose date is less than or equal to the current date.

query AllArticles($currentDate: Date!) {
allMdx(
filter: {
frontmatter: { published: { eq: true }, date: { lte: $currentDate } }
}
sort: { fields: [frontmatter___date], order: DESC }
) {
edges {
node {
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
}
}
}
}
}

But now we've gotta pass in that $currentDate variable, so open up your gatsby-node.js file and add the following:

// gatsby-node.js
exports.onCreatePage = ({ page, actions }) => {
const { createPage, deletePage } = actions
deletePage(page)
createPage({
...page,
context: {
...page.context,
currentDate: getCurrentDate(),
},
})
}

onCreatePage is provided by Gatsby and is called whenever new pages are created. By overriding it here, we can pass additional data (currentDate) via its context object that'll be made available to all downstream components.

Here's the getCurrentDate implementation:

// gatsby-node.js
/**
* Returns the current date in YYYY-MM-DD format
*/
function getCurrentDate() {
const d = new Date()
let month = (d.getMonth() + 1).toString()
if (month.length < 2) {
month = `0${month}`
}
let day = d.getDate().toString()
if (day.length < 2) {
day = `0${day}`
}
return `${d.getFullYear()}-${month}-${day}`
}

One More Thing...

In my Add Previous and Next Article Buttons in Gatsby article I show how to add "Previous Article" and "Next Article" buttons that depend on the published flag. For this date filtering, I needed to update this to account for the date constraint, so posts that are set to be released in the future don't appear in the pagination links before they're ready.

// gatsby-node.js
const publishedPosts = posts
.filter(post => {
return (
post.node.frontmatter.published === true &&
new Date(post.node.frontmatter.date) <= new Date(getCurrentDate())
)
})
.reduce((acc, post) => {
acc[post.node.fields.slug] = post
return acc
}, {})

Wrapping Up

With this new logic in place, I can write as much content as I want and set it to publish on a certain date. The only thing I need to do is rebuild and deploy my site (I'm looking for a way to automate this...)