Gatsby.js ブログへのタグ追加

こんにちは! うしじです。

本ブログにタグを追加したので、その方法について記載します。


参考:Gatsby公式ドキュメント

タグを追加するにあたり、下記のGatsbyのドキュメントを参考にしました。基本的には、下記のページに沿って対応すれば、タグを追加できます。



Step 1. Markdownファイルへのタグの追加

まず、ブログの各Markdownファイルにタグを追加します。本記事では下記のような記載です。

---
template: BlogPost
path: /blog-tags
date: 2020-05-19T15:00:00+09:00
title: Gatsby.js ブログへのタグ追加
thumbnail: /assets/202005/20200519/tag-3425877_1280.jpg
metaDescription:  "GatsbyJSで作成したブログにタグを追加したので、その方法について記載しています。"
tags: ["GatsbyJS"]
---


Step 2. タグページ(/tags/{tag}) のテンプレート作成

src/templates/tags.jsファイルを新たに作成し、タグページのテンプレートを作成します。 本ブログの場合は、fields.slugを作成していなかったので、Gatsbyのドキュメントの例(Creating Tags Pages for Blog Posts)から少し変更しています。

import React from "react"
import PropTypes from "prop-types"
import Helmet from 'react-helmet'
import { graphql } from "gatsby"
import Layout from "../components/layout"
import PostLink from "../components/post-link"


const Tags = ({ pageContext, data }) => {
  const { tag } = pageContext
  const { edges, totalCount } = data.allMarkdownRemark
  const tagHeader = `"${tag}" 関連記事: ${totalCount}`
  const Posts = edges.map(edge => <PostLink key={edge.node.id} post={edge.node} />)

  return (
    <Layout>      
    <div>
      <h1>{tagHeader}</h1>
      <div className="grids">
        {Posts}
      </div>
    </div>
    </Layout>
  )
}

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({
              path: PropTypes.string.isRequired,
              title: 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 {
          id
          excerpt(pruneLength: 250)
          frontmatter {
            date(formatString: "MMMM DD, YYYY")
            title
            path
            thumbnail
            tags
          }
        }
      }
    }
  }
`


Step 3. gatsby-node.js の更新

タグページのテンプレートを用いてページを作成するために、gatsby-node.js を更新します。これで、各タグのページを表示できるようになります。
本ブログの場合だと、このページのことです。

const path = require(`path`)
const _ = require(`lodash`)

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions

  const blogPostTemplate = path.resolve(`src/templates/blogTemplate.js`)
  const tagTemplate = path.resolve(`src/templates/tags.js`)

  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            id
            frontmatter {
              path
              tags
            }
          }
        }
      }
      tagsGroup: allMarkdownRemark(limit: 2000) {
        group(field: frontmatter___tags) {
          fieldValue
        }
      }
    }
  `)

  // Handle errors
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.frontmatter.path,
      component: blogPostTemplate,
      context: {}, // additional data can be passed via context
    })
  })

  result.data.tagsGroup.group.forEach(tag => {
    createPage({
      path: `/tags/${_.kebabCase(tag.fieldValue)}`,
      component: tagTemplate,
      context: {
        tag: tag.fieldValue,
      },
    })
  })

}


Step 4. タグ一覧ページ作成

次に、src/pages/tags.jsファイルを作成し、タグの一覧ページを作成します。
本ブログの場合だと、このページのことです。

import React from "react"
import PropTypes from "prop-types"
import kebabCase from "lodash/kebabCase"
import { Helmet } from "react-helmet"
import { Link, graphql } from "gatsby"
import Layout from "../components/layout"

const TagsPage = ({
  data: {
    allMarkdownRemark: { group },
    site: {
      siteMetadata: { title },
    },
  },
}) => (
  <Layout>
    <Helmet>
        <title>{title}</title>
    </Helmet>
    <div>
      <h1>タグ一覧</h1>
      <ul class="tags-list">
        {group.map(tag => (
          <li key={tag.fieldValue}>
            <Link to={`/tags/${kebabCase(tag.fieldValue)}`}>
              {tag.fieldValue} ({tag.totalCount})
            </Link>
          </li>
        ))}
      </ul>
    </div>
  </Layout>
)

TagsPage.propTypes = {
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      group: PropTypes.arrayOf(
        PropTypes.shape({
          fieldValue: PropTypes.string.isRequired,
          totalCount: PropTypes.number.isRequired,
        }).isRequired
      ),
    }),
    site: PropTypes.shape({
      siteMetadata: PropTypes.shape({
        title: PropTypes.string.isRequired,
      }),
    }),
  }),
}

export default TagsPage

export const pageQuery = graphql`
  query {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(limit: 2000) {
      group(field: frontmatter___tags) {
        fieldValue
        totalCount
      }
    }
  }
`


Step 5. 各ブログにタグを表示

最後に、下記の画像のように、各ブログにタグを表示させます。このタグは、各タグページへのリンクになっています。

各ブログのタグイメージ
import React from "react"
import { Link } from "gatsby"
import kebabCase from "lodash/kebabCase"

const PostLink = ({ post }) => (
  <article className="card ">
    <Link to={post.frontmatter.path}>
      {!!post.frontmatter.thumbnail && (
        <img src={post.frontmatter.thumbnail} alt={post.frontmatter.title + "- Featured Shot"} />
      )}
    </Link>
    <header>
      <h2 className="post-title">
        <Link to={post.frontmatter.path} className="post-link">
          {post.frontmatter.title}
        </Link>
      </h2>
      <div className="post-meta">{post.frontmatter.date}</div>
      <div className="post-meta">        
        {post.frontmatter.tags.map(tag => {
          return <span className="post-tag"><Link 
            key={tag}
            to={`/tags/${kebabCase(tag)}`}>
              #{tag}
            </Link></span>
        })}
      </div>
    </header>
  </article>
)
export default PostLink


これでタグの追加完了です!