Categories
Gatsby.js

Gatsby.js — Tags and Post Page

Spread the love

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Tags Pages for Blog Posts

We can create tag pages for blog posts.

To do this, we work on a few files:

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ],
}

gatsby-node.js

const path = require("path")
const _ = require("lodash")
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}
exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = path.resolve("src/templates/post.js")
  const tagTemplate = path.resolve("src/templates/tags.js")
  const result = await graphql(`
    {
      postsRemark: allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 2000
      ) {
        edges {
          node {
            fields {
              slug
            }
            frontmatter {
              tags
            }
          }
        }
      }
      tagsGroup: allMarkdownRemark(limit: 2000) {
        group(field: frontmatter___tags) {
          fieldValue
        }
      }
    }
  `)
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  const posts = result.data.postsRemark.edges
  posts.forEach(({ node }) => {
    const slug = node.fields.slug
    createPage({
      path: slug,
      component: blogPostTemplate,
      context: { slug },
    })
  })
  const tags = result.data.tagsGroup.group
  tags.forEach(tag => {
    createPage({
      path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
      component: tagTemplate,
      context: {
        tag: tag.fieldValue,
      },
    })
  })
}

src/templates/tags.js

import React from "react"
import PropTypes from "prop-types"
import { Link, graphql } from "gatsby"

const Tags = ({ pageContext, data }) => {
  const { tag } = pageContext
  const { edges, totalCount } = data.allMarkdownRemark
  const tagHeader = `${totalCount} post${totalCount === 1 ? "" : "s"
    } tagged with "${tag}"`
  return (
    <div>
      <h1>{tagHeader}</h1>
      <ul>
        {edges.map(({ node }) => {
          const { slug } = node.fields
          const { title } = node.frontmatter
          return (
            <li key={slug}>
              <Link to={slug}>{title}</Link>
            </li>
          )
        })}
      </ul>
      <Link to="/tags">All tags</Link>
    </div>
  )
}
Tags.propTypes = {
  pageContext: PropTypes.shape({
    tag: PropTypes.string.isRequired,
  }),
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      totalCount: PropTypes.number.isRequired,
      edges: PropTypes.arrayOf(
        PropTypes.shape({
          node: PropTypes.shape({
            frontmatter: PropTypes.shape({
              title: PropTypes.string.isRequired,
            }),
            fields: PropTypes.shape({
              slug: PropTypes.string.isRequired,
            }),
          }),
        }).isRequired
      ),
    }),
  }),
}
export default Tags
export const pageQuery = graphql`
  query($tag: String) {
    allMarkdownRemark(
      limit: 2000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  }
`

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"

export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    <div>
      <h1>{post.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </div>
  )
}
export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`

src/content/post.md

---
title: "A Trip To the Zoo"
tags: ["animals", "Chicago", "zoos"]
date: "2020-01-01"
---
I went to the zoo today. It was terrible.

In gatsby-config.js , we add the gatsby-source-filesystem plugin to read the Markdown files from the src/content folder.

And we add the gatsby-transformer-remark plugin to transform the Markdown to HTML.

Next, in gatsby-node.js , we create the slug field with the onCreateNode method.

We call the createFilePath function to create the slug field value from the file path.

Then we call createNodeField to create the slug field with the object with name set to 'slug' .

The value is the slug string that we created from createFilePath .

In the createPages function, we maker the query to get the post data.

The result.data.postRemark.edges has the posts data.

We call forEach on it so that we can call createPage on each entry in the callback to create the pages.

node.field.slug has the slug value we created from onCreateNode .

component is set to the blogPostTemplate so that we can display the site.

Then to get the tags, we use the result.data.tagsGroup.group property.

We pass in the slug to the context property of in the first forEach to get that data to the page.

We call forEach with a callback that calls the createPage function again.

But this time, we use it to create the tags page.

The path is the URL for the tags page.

component has the template we use to render the tags page.

context has the data we want to render on the tags page.

In tags.js , we have the tags page template.

We have the pageQuery object to make the query for the items with the given tag.

Then in the tags component, we get all the returned data from the data prop.

edges have the items with the given tag.

We call map to render the returned content.

The slug property has the slug. title has the title.

We have the Link component for each entry so we can go to the page with the given slug.

In post.js , we render the page.

We get the data from the data prop’s markdownRemark property.

Then we see the data displayed.

We have the query object to make the query to get the post with the given slug.

Conclusion

We can add a tags page to display the Markdown file with the given tags with a post page to display the Markdown file with a given slug with Gatsby.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *