Gatsbyの投稿へタグを追加する

#gatsby #tag

March 26, 2023

投稿にタグを追加する。タグ一覧ページとタグページを追加する。

目次


投稿のMarkdownへタグを追加する

---
title: Gatsbyにタグを追加する  
date: "2023-03-26"
tags: ["gatsby", "plugin", "test"]  
---

GraphQLで投稿のタグを取得する

Gatsby起動後にGraphQLのURLにアクセスしてタグデータを取得する。
http://localhost:8000/___graphql

Qrery

投稿をtagsでグループ化し、タグがついた投稿の数をtotalCountで取得する。

{
  allMarkdownRemark {
    group(field: { frontmatter: { tags: SELECT } }) {
      tag: fieldValue
      totalCount
    }
  }
}

Result

タグと投稿の数が取得できます。

{
  "data": {
    "allMarkdownRemark": {
      "group": [
        {
          "tag": "gatsby",
          "totalCount": 1
        },
        {
          "tag": "plugin",
          "totalCount": 1
        },
        {
          "tag": "test",
          "totalCount": 1
        }
      ]
    }
  },
  "extensions": {}
}

タグページのテンプレートを作成する(/tags/{tag_name})

選択したタグを含む投稿一覧を表示するテンプレートページを作成する。
ここではテンプレートを作成し、実際のタグごとのページは、後述のgatsby-node.jscreatePagesで生成される。
タグページアクセスURLは、localhost:8000/tags/{tag_name}/

作成ファイル : src/templates/tags.js

import React from "react"
import PropTypes from "prop-types"

// Components
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>
      {/*
              This links to a page that does not yet exist.
              You'll come back to it!
            */}
      <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: { frontmatter: { date: DESC }}
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  }
`

gatsby-node.jsにタグページを生成する記述を追加する

lodashライブラリを追加する。
lodashライブラリのkebabCaseファンクションを利用してタグをケバブケースに変更する。
タグ名abc_defabcDefのURLは、/tags/abc-defになる。

gatsby-node.jsの必要な箇所へタグページを生成する処理を記述する

// ライブラリ読み込み
const _ = require("lodash")

// 以降は、createPagesへ追記

// テンプレートページの読み込み
const tagTemplate = path.resolve("src/templates/tags.js")

// GrapshQLへタグデータ取得処理を追加
tagsGroup: allMarkdownRemark(limit: 2000) {
  group(field: { frontmatter: { tags: SELECT }}) {
    fieldValue
  }
}

// タグテンプレートとタグデータからページを生成する
// Extract tag data from query
const tags = result.data.tagsGroup.group
// Make tag pages
tags.forEach(tag => {
  createPage({
    path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
    component: tagTemplate,
    context: {
      tag: tag.fieldValue,
    },
  })
})

タグのインデックス(一覧)ページを作成する

タグのインデックスページを作成する。
アクセスURLは、http://localhost:8000/tags/

作成ファイル : src/pages/tags.js
template/tags.jsとは別のファイル

import React from "react"
import PropTypes from "prop-types"

// Utilities
import kebabCase from "lodash/kebabCase"

// Components
import { Helmet } from "react-helmet"
import { Link, graphql } from "gatsby"

const TagsPage = ({
    data: {
        allMarkdownRemark: { group },
        site: {
            siteMetadata: { title },
        },
    },
}) => (
<div>
    <Helmet title={title} />
    <div>
    <h1>Tags</h1>
    <ul>
        {group.map(tag => (
        <li key={tag.fieldValue}>
            <Link to={`/tags/${kebabCase(tag.fieldValue)}/`}>
            {tag.fieldValue} ({tag.totalCount})
            </Link>
        </li>
        ))}
    </ul>
    </div>
</div>
)

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
        }
    }
}
`

参考ページ


Profile picture

Written by hikn 東京でエンジニアとして働いています。 Twitter