Gatsbyブログにタグを追加

2021.05.04     2021.05.06

Gatsby公式のstarterであるgatsby-starter-blogをベースに、 公式ページではあまり記載されていないタグ機能を追加する。

fontmatterにtags欄を追加する

前提として、入稿するMarkdownファイルのfrontmatterに配列でtags欄を追加する。

index.md
---
title: Gatsbyブログにタグを追加
date: 2021-05-04
tags: [Gatsby, Blog]---

タグごとの記事リストページの作成

以下のページを参考に、タグごとに記事のリストができるように作成。

ページネーション機能ができるようにしたindex.jsをベースに、新たにtag-list.jsを作成。

差分は、タグ名を呼び出す箇所とGraphQLのクエリにタグ名でフィルターをかけるところ。 ページ付けするところは、新たにタグページ用にsrc/components/pagination-tag.jsを用意する。

src/templates/tag-list.js
...
import TagPagenation from "../components/pagination-tag"
const BlogIndex = ({ data, location, pageContext }) => {
 const siteTitle = data.site.siteMetadata?.title || `Title`
 const posts = data.allMarkdownRemark.nodes
 const { tag } = pageContext
...

  return (
   <Layout location={location} title={siteTitle}>
     <Seo title={tag} />
     <h2>{tag}</h2>
...

   <TagPagenation pageContext={pageContext} />   </Layout>
 )
}

export const pageQuery = graphql`
 query ($skip: Int!, $limit: Int!, $tag: String!) {
   site {
     siteMetadata {
       title
     }
   }
   allMarkdownRemark(
     sort: { fields: [frontmatter___date], order: DESC }
     filter: {frontmatter: {tags: {in: [$tag]}}}     skip: $skip
     limit: $limit
     ) {
     nodes {
       excerpt
       fields {
         slug
       }
       frontmatter {
         date(formatString: "YYYY.MM.DD")
         title
         description
       }
     }
   }
 }
`
src/components/pagination-tag.js
import React from "react"
import { navigate } from "gatsby"
import { makeStyles } from "@material-ui/core/styles"
import { Pagination } from "@material-ui/lab"
import kebabCase from 'lodash/kebabCase'

const useStyles = makeStyles({
  root: {
    display: `flex`,
    flexWrap: `wrap`,
    justifyContent: `center`,
    alignItems: "center",
  },
});

const TagPagenation = ({ pageContext }) => {
  const classes = useStyles()
  const { tag, numberOfPages, humanPageNumber } = pageContext

  const handleChange = (_event, value) => {
    value === 1 ? navigate(`/tags/${kebabCase(tag)}/`) 
                : navigate(`/tags/${kebabCase(tag)}/${value}`)
  }
  return (
    <div className={classes.root}>
      <Pagination
        variant="outlined"
        defaultPage={humanPageNumber}
        count={numberOfPages}
        onChange={handleChange}
      />
    </div>
  )
}
export default TagPagenation

gatsby-node.jsでもGraphQLのクエリを修正。kebab caseを使うため、lodashライブラリを使用。

result.data.tags.groupからページを作るところと、frontmatterのtagsの型を[String]にするところに注意。

gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const { paginate } = require(`gatsby-awesome-pagination`)
const _ = require("lodash")
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions

  // Define a template for blog post
  const blogPost = path.resolve(`./src/templates/blog-post.js`)

  // Get all markdown blog posts sorted by date
  const result = await graphql(
    `
      {
        allMarkdownRemark(
          sort: { fields: [frontmatter___date], order: ASC }
          limit: 1000
        ) {
          nodes {
            id
            fields {
              slug
            }
          }
        }

        tags: allMarkdownRemark(limit: 1000) {          group(field: frontmatter___tags) {            fieldValue            nodes {              fields {                slug              }            }          }        }      }
    `
  )

  if (result.errors) {
    reporter.panicOnBuild(
      `There was an error loading your blog posts`,
      result.errors
    )
    return
  }

  const posts = result.data.allMarkdownRemark.nodes
  const tags = result.data.tags.group
  ...

  // Create tag pages  tags.forEach((tag) => {    paginate({      createPage,      items: tag.nodes,      itemsPerPage: 10,      pathPrefix: `/tags/${_.kebabCase(tag.fieldValue)}`,      component: path.resolve('src/templates/tag-list.js'),      context: {        tag: tag.fieldValue      }    })  })}

 ...

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions

  createTypes(`

 ...

     type Frontmatter {
      title: String
      description: String
      date: Date @dateformat
      tags: [String]    }

    type Fields {
      slug: String
    }
  `)
}

タグ一覧のページの作成

すべてのタグがリストになるページとしてsrc/pages/tags.jsを作成。

src/pages/tags.js
import * as React from "react"
import { Link, graphql } from "gatsby"
import kebabCase from 'lodash/kebabCase'

import Layout from "../components/layout"
import Seo from "../components/seo"

const TagsPage = ({
    location,
    data: {
      allMarkdownRemark: { group },
      site: {
        siteMetadata: { title },
      },
    },
  }) => {
    
  return (  
    <Layout location={location} title={title}>
      <Seo title="Tags" />
      <h1>All Tags</h1>
      <div className="tag-list">
      <ul>
        {group.map(tag => (
          <li key={tag.fieldValue}>
            <Link to={`/tags/${kebabCase(tag.fieldValue)}/`}>
              {tag.fieldValue} ({tag.totalCount})
            </Link>
          </li>
        ))}
      </ul>
      </div>
    </Layout>
   )
}

export default TagsPage

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

記事に使用しているタグを表示

src/templates/blog-post.jsに以下の部分を追加して、使用しているタグを表示。

src/templates/blog-post.js
<ul>
  {post.frontmatter.tags && post.frontmatter.tags.map((tag) => (
     <li>
       <Link to={`/tags/${kebabCase(tag)}`}>{tag}</Link>
     </li>
   ))}
</ul>

fontmatterにtags欄がないときは表示されないようにしている。


Recent Posts