Yo! You might want to check out this article first on Managing Draft Articles in Gatsby.
Lots of sites include "Previous Article" and "Next Article" buttons/links on the bottom of their articles. I wanted some, too!
The Solution
Updating the UI
Let's start with the fun stuff first - updating the UI. I've got a template that renders the actual markdown content, and I want to add the previous and next buttons to it:
// templates/article.jsclass ArticleTemplate extends React.Component {// These represent the previous and next articles...// ... we'll pass these via GraphQL later onconst { previous, next } = this.props.pageContext// Boolean letting us know if we should show prev and next linksconst needsPagination = Boolean(previous) || Boolean(next)render() {// Imagine the article's body is rendered here...{needsPagination && (<Pagination>{previous && (<Link to={previous.fields.slug}><span>← {previous.frontmatter.title}</span></Link>)}{next && (<Link to={next.fields.slug}><span>{next.frontmatter.title} →</span></Link>)}</Pagination>)}}}
(Pagination
is a styled component - here's the source if you're interested.)
Refresh your site and you'll see... nothing new! We haven't passed the previous and next articles via GraphQL yet, so needsPagination
will be false
and that conditional won't render anything. Let's fix that.
Updating The Content Pipeline
Note: I've made some major edits to my gatsby-node.js
, but the general idea translates to whatever you've got.
I'm also excluding all the logic that's not related to previous and next articles.
// gatsby-node.jsfunction createArticlePages() {return new Promise((resolve, reject) => {const articleTemplate = path.resolve(`./src/templates/article.js`)resolve(query.then(result => {const articles = result.data.allMdx.edges/*** We only want to check published articles when* fetching the `previous` and `next` ones. So,* let's convert the `articles` array into an object* whose keys are the article slugs.** You don't need this step if you're not setting a* `published` flag on articles.*/const publishedArticles = articles.filter(article => {return article.node.frontmatter.published === true}).reduce((acc, article) => {acc[article.node.fields.slug] = articlereturn acc}, {})articles.forEach((article, index) => {const [previous, next] = getPrevAndNextArticles(publishedArticles,article.node.fields.slug)createPage({path: article.node.fields.slug,component: blogArticleTemplate,context: {path: article.node.fields.slug,previous: previous ? previous.node : null,next: next ? next.node : null,},})}))})}
Most of this is standard Gatsby stuff, but I'm taking an extra step to only fetch the previous and next articles from the subset of published articles (you can skip this step if you don't have a published
flag for your articles).
If you're wondering why I'm only using publishedArticles
to fetch the previous
and next
articles, and not using it instead of articles
in the forEach
below, it's because I want to create article pages regardless of their published
status - I just don't want to link to unpublished ones.
Near the bottom of the example is where we pass the previous
and next
articles via Gatsby's context object, making them available to our Article
template.
Let's take a look at the getPrevAndNextArticles
function:
function getPrevAndNextArticles(articles, slug) {const currentArticleIndex = Object.keys(articles).findIndex(slug => slug === slug)const articlesArray = Object.values(articles)let prevArticlelet nextArticleif (currentArticleIndex < articlesArray.length - 1) {prevArticle = articlesArray[currentArticleIndex + 1]}if (currentArticleIndex > 0) {nextArticle = articlesArray[currentArticleIndex - 1]}return [prevArticle, nextArticle]}
This function takes an object of articles
(where each key is the article's slug) and the slug
of the article we're currently iterating over in the forEach
loop of createArticlePages
. We need to find the index of the current article in relation to its siblings, and then we can return the previous and next articles. If the current article is either the first or the last one in the collection, previous
or next
will be null, respectively.
Now when you refresh your site, you should see previous and next article links (assuming you have enough published articles!)