Add Tags to Gatsby

August 06, 2019

Update graphql query that gets all of the blogs posts to get tags under edges.node.frontmatter like so:

// gatsby-node.js
const result = await graphql(
  `
    {
      allMarkdownRemark(
        sort: { fields: [frontmatter___date], order: DESC }
        limit: 1000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              title
              tags
            }
          }
        }
      }
    }
  `
)

First you need to gather all of the uniq tags and then iterate through tags and create page. When you create the page you need to pass the tag into the page context. In the example below, we are also passing pagination.

// gatsby-node.js
// TOP of the file
const _ = require("lodash")

// the rest of the code is inside of: exports.createPages = async ({ graphql, actions }) => {
const tags = _.reduce(result.data.allMarkdownRemark.edges, (results, edge) => {
  const tags = _.get(edge, "node.frontmatter.tags");
  if (tags) {
    _.each(tags, tag => {
      if(!results[tag]) {
        results[tag] = 0;
      }

      results[tag] += 1
    })
  }

  return results;
}, {});

Object.keys(tags).forEach(tag => {
  const tagNumPages = Math.ceil(tags[tag] / postsPerPage);

  Array.from({ length: tagNumPages }).forEach((_, i) => {
    createPage({
      path: i === 0 ? `/tags/${tag}` : `/tags/${tag}/${i + 1}`,
      component: path.resolve("./src/templates/tags.js"),
      context: {
        limit: postsPerPage,
        skip: i * postsPerPage,
        numPages: tagNumPages,
        currentPage: i + 1,
        tag,
      },
    })
  })
})

The last part is to create the tags template which can be found below:

// src/templates/tags.js
import React from "react"
import { Link, graphql, useStaticQuery } from "gatsby"
import Layout from "../components/layout"

class Tags extends React.Component {
  render() {
    const { data, pageContext } = this.props
    const posts = data.allMarkdownRemark.edges;
    const { currentPage, numPages, tag } = pageContext
    const isFirst = currentPage === 1
    const isLast = currentPage === numPages
    const prevPage = `/tags/${tag}${(currentPage - 1 === 1 ? '' : (currentPage - 1).toString())}`;
    const nextPage = `/tags/${tag}${(currentPage + 1).toString()}`;

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <div>
          {posts.forEach(node => {
            return (<div key={node.fields.slug}>{node.frontmatter.title}</div>)
          })}
        </div>
        <ul
            style={{
              display: 'flex',
              flexWrap: 'wrap',
              justifyContent: 'space-between',
              alignItems: 'center',
              listStyle: 'none',
              padding: 0,
            }}
          >
            {!isFirst && (
              <Link to={prevPage} rel="prev">
                ← Previous Page
              </Link>
            )}
            {Array.from({ length: numPages }, (_, i) => (
              <li
                key={`pagination-number${i + 1}`}
                style={{
                  margin: 0,
                }}
              >
                <Link
                  to={`/tags/${tag}/${i === 0 ? '' : i + 1}`}
                  style={{
                    padding: rhythm(1 / 4),
                    textDecoration: 'none',
                    color: i + 1 === currentPage ? '#ffffff' : '',
                    background: i + 1 === currentPage ? '#007acc' : '',
                  }}
                >
                  {i + 1}
                </Link>
              </li>
            ))}
            {!isLast && (
              <Link to={nextPage} rel="next">
                Next Page →
              </Link>
            )}
          </ul>
      </Layout>
    )
  }
}

export default Tags

export const pageQuery = graphql`
   query($skip: Int, $limit: Int, $tag: String) {
    allMarkdownRemark(
      filter: { frontmatter: { tags: { in: [$tag] } } }
      sort: { fields: [frontmatter___date], order: DESC },
      limit: $limit,
      skip: $skip
    ) {
      edges {
        node {
          excerpt
          fields {
            slug
          }
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            title
            description
            tags
          }
        }
      }
    }
  }
`

Search