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欄がないときは表示されないようにしている。